# Always print this out before your assignment
sessionInfo()
getwd()

# load all your libraries in this chunk 
library('tidyverse')
library("fs")
library('here')
library('dplyr')
library('tidyverse')
library('ggplot2')
library('ggrepel')
library('ggthemes')
library('forcats')
library('rsample')
library('lubridate')
library('ggthemes')
library('kableExtra')
library('pastecs')
library('viridis')
library('plotly')
library('tidyquant')
library('scales')
library("gdata")

# note, do not run install.packages() inside a code chunk. install them in the console outside of a code chunk. 

Part 1 - Final Project Cleaning and Summary Statistics

1a) Loading data


#Reading the data in and doing minor initial cleaning in the function call
#Reproducible data analysis should avoid all automatic string to factor conversions.
#strip.white removes white space 
#na.strings is a substitution so all that have "" will = na
data <- read.csv(here::here("final_project", "donor_data.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")

1b) Fixing the wonky DOB & Data cleanup


#(Birthdate and Age, ID as a number)adding DOB (Age/Spouse Age) in years columns and adding two fields for assignment and number of children and number of degrees
dataclean <- data %>%
  mutate(Birthdate = ifelse(Birthdate == "0001-01-01", NA, Birthdate)) %>%
  mutate(Birthdate = mdy(Birthdate)) %>%
  mutate(Age = as.numeric(floor(interval(start= Birthdate, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  mutate(Spouse.Birthdate = ifelse(Spouse.Birthdate == "0001-01-01", NA, Spouse.Birthdate)) %>%
  mutate(Spouse.Birthdate = mdy(Spouse.Birthdate)) %>%
  mutate(Spouse.Age = as.numeric(floor(interval(start= Spouse.Birthdate,
                                                end=Sys.Date())/duration(n=1, unit="years")))) %>%
  mutate(ID = as.numeric(ID)) %>% 
  mutate(Assignment_flag = ifelse(is.na(Assignment.Number), 0,1)) %>% 
  mutate( No_of_Children = ifelse(is.na(Child.1.ID),0,
                            ifelse(is.na(Child.2.ID),1,2)))%>%
 mutate(ID = as.numeric(ID)) %>% 
    mutate( nmb_degree = ifelse(is.na(Degree.Type.1),0,
                            ifelse(is.na(Degree.Type.2),1,2)))
#conferral dates
dataclean <- dataclean %>%
  
  mutate(Conferral.Date.1 = ifelse(Conferral.Date.1 == "0001-01-01", NA, Conferral.Date.1)) %>%
  mutate(Conferral.Date.1 = mdy(Conferral.Date.1)) %>%
  mutate(Conferral.Date.1.Age = as.numeric(floor(interval(start= Conferral.Date.1, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
  mutate(Conferral.Date.2 = ifelse(Conferral.Date.2 == "0001-01-01", NA, Conferral.Date.2)) %>%
  mutate(Conferral.Date.2 = mdy(Conferral.Date.2)) %>%
  mutate(Conferral.Date.2.Age = as.numeric(floor(interval(start= Conferral.Date.2, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
  mutate(Last.Contact.By.Anyone = ifelse(Last.Contact.By.Anyone == "0001-01-01", NA, Last.Contact.By.Anyone)) %>%
  mutate(Last.Contact.By.Anyone = mdy(Last.Contact.By.Anyone)) %>%
  mutate(Last.Contact.Age = as.numeric(floor(interval(start= Last.Contact.By.Anyone, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
 mutate(HH.First.Gift.Date = ifelse(HH.First.Gift.Date == "0001-01-01", NA, HH.First.Gift.Date)) %>%
  mutate(HH.First.Gift.Date = mdy(HH.First.Gift.Date)) %>%
mutate(HH.First.Gift.Age = as.numeric(floor(interval(start= HH.First.Gift.Date, end=Sys.Date())/duration(n=1, unit="years"))))

#major gift 
dataclean <- 
  dataclean %>% 
  mutate(major_gifter = ifelse(Lifetime.Giving > 50000, 1,0) %>% factor(., levels = c("0","1")))


#splitting up the age into ranges and creating category for easy visualization 
dataclean <- dataclean %>%
  mutate(age_range = 
    ifelse(Age %in% 10:19, "10 < 20 years old",
    ifelse(Age %in% 20:29, "20 < 30 years old", 
    ifelse(Age %in% 30:39, "30 < 40 years old",
    ifelse(Age %in% 40:49, "40 < 50 years old",
    ifelse(Age %in% 50:59, "50 < 60 years old",
    ifelse(Age %in% 60:69, "60 < 70 years old",
    ifelse(Age %in% 70:79, "70 < 80 years old",
    ifelse(Age %in% 80:89, "80 < 90 years old",
    ifelse(Age %in% 90:120, "90+ years old",
    NA))))))))))


#seeing what we have
table(dataclean$age_range)

10 < 20 years old 20 < 30 years old 30 < 40 years old 
             3985             24558             21037 
40 < 50 years old 50 < 60 years old 60 < 70 years old 
            16851             20755             18257 
70 < 80 years old 80 < 90 years old     90+ years old 
            12246              5984              6633 
#50-60 is the most common age range 

#creating a region column using the county data and the OMB MSA (Metropolitan Statistical Area) definitions

dataclean <- dataclean %>%
  mutate(region = 
    ifelse(County == "San Luis Obispo" & State == "CA", "So Cal",
    ifelse(County == "Kern" & State == "CA", "So Cal",
    ifelse(County == "San Bernardino" & State == "CA", "So Cal",
    ifelse(County == "Santa Barbara" & State == "CA", "So Cal",
    ifelse(County == "Ventura" & State == "CA", "So Cal",
    ifelse(County == "Los Angeles" & State == "CA", "So Cal",
    ifelse(County == "Orange" & State == "CA", "So Cal",
    ifelse(County == "Riverside" & State == "CA", "So Cal",
    ifelse(County == "San Diego" & State == "CA", "So Cal",
    ifelse(County == "Imperial" & State == "CA", "So Cal",
    ifelse(County == "King" & State == "WA", "Seattle",
    ifelse(County == "Snohomish" & State == "WA", "Seattle",
    ifelse(County == "Pierce" & State == "WA", "Seattle",
    ifelse(County == "Clackamas" & State == "OR", "Portland",
    ifelse(County == "Columbia" & State == "OR", "Portland",
    ifelse(County == "Multnomah" & State == "OR", "Portland",
    ifelse(County == "Washington" & State == "OR", "Portland",
    ifelse(County == "Yamhill" & State == "OR", "Portland",
    ifelse(County == "Clark" & State == "WA", "Portland",
    ifelse(County == "Skamania" & State == "WA", "Portland",
    ifelse(County == "Denver" & State == "CO", "Denver",
    ifelse(County == "Arapahoe" & State == "CO", "Denver",
    ifelse(County == "Jefferson" & State == "CO", "Denver",
    ifelse(County == "Adams" & State == "CO", "Denver",
    ifelse(County == "Douglas" & State == "CO", "Denver",
    ifelse(County == "Broomfield" & State == "CO", "Denver",    
    ifelse(County == "Elbert" & State == "CO", "Denver",
    ifelse(County == "Park" & State == "CO", "Denver",
    ifelse(County == "Clear Creek" & State == "CO", "Denver",
    ifelse(County == "Alameda" & State == "CA", "Bay Area",
    ifelse(County == "Contra Costa" & State == "CA", "Bay Area",
    ifelse(County == "Marin" & State == "CA", "Bay Area",
    ifelse(County == "Monterey" & State == "CA", "Bay Area",
    ifelse(County == "Napa" & State == "CA", "Bay Area",
    ifelse(County == "San Benito" & State == "CA", "Bay Area",
    ifelse(County == "San Francisco" & State == "CA", "Bay Area",
    ifelse(County == "San Mateo" & State == "CA", "Bay Area",
    ifelse(County == "Santa Clara" & State == "CA", "Bay Area",
    ifelse(County == "Santa Cruz" & State == "CA", "Bay Area",
    ifelse(County == "Solano" & State == "CA", "Bay Area",
    ifelse(County == "Sonoma" & State == "CA", "Bay Area",
           NA))))))))))))))))))))))))))))))))))))))))))

dataclean <- dataclean %>%
  mutate(region = 
    ifelse(County == "Kings" & State == "NY", "New York",
    ifelse(County == "Queens" & State == "NY", "New York",
    ifelse(County == "New York" & State == "NY", "New York",
    ifelse(County == "Bronx" & State == "NY", "New York",
    ifelse(County == "Richmond" & State == "NY", "New York",
    ifelse(County == "Westchester" & State == "NY", "New York",
    ifelse(County == "Bergen" & State == "NY", "New York",
    ifelse(County == "Hudson" & State == "NY", "New York",
    ifelse(County == "Passaic" & State == "NY", "New York",
    ifelse(County == "Putnam" & State == "NY", "New York",
    ifelse(County == "Rockland" & State == "NY", "New York",
    ifelse(County == "Suffolk" & State == "NY", "New York",
    ifelse(County == "Nassau" & State == "NY", "New York",
    ifelse(County == "Middlesex" & State == "NJ", "New York",
    ifelse(County == "Monmouth" & State == "NJ", "New York",
    ifelse(County == "Ocean" & State == "NJ", "New York",
    ifelse(County == "Somerset" & State == "NJ", "New York",
    ifelse(County == "Essex" & State == "NJ", "New York",
    ifelse(County == "Union" & State == "NJ", "New York",
    ifelse(County == "Morris" & State == "NJ", "New York",
    ifelse(County == "Sussex" & State == "NJ", "New York",
    ifelse(County == "Hunterdon" & State == "NJ", "New York",
    ifelse(County == "Pike" & State == "NJ", "New York",
    region))))))))))))))))))))))))


# code nor cal region as all others in CA not already defined

dataclean <- dataclean %>%
  mutate(region = 
    ifelse(State == "CA" & is.na(region) == TRUE, "Nor Cal", region))


#Removing Columns that provide no benefit 

dataclean <- subset(dataclean,select = -c(Assignment.Number
                                                    ,Assignment.has.Historical.Mngr
                                                    ,Suffix
                                                    ,Assignment.Date
                                                    ,Assignment.Manager
                                                    ,Assignment.Role
                                                    ,Assignment.Title
                                                    ,Assignment.Status
                                                    ,Strategy
                                                    ,Progress.Level
                                                    ,Assignment.Group
                                                    ,Assignment.Category
                                                    ,Funding.Method
                                                        ,Expected.Book.Date
                                                        ,Qualification.Amount
                                                        ,Expected.Book.Amount
                                                        ,Expected.Book.Date
                                                        ,Hard.Gift.Total
                                                        ,Soft.Credit.Total
                                                        ,Total.Assignment.Gifts
                                                        ,No.of.Pledges
                                                        ,Proposal..
                                                        ,Proposal.Notes
                                                        ,HH.Life.Spouse.Credit
                                                        ,Last.Contact.By.Manager
                                                        ,X..of.Contacts.By.Manager
                                                        ,DonorSearch.Range
                                                        ,iWave.Range
                                                        ,WealthEngine.Range
                                                        ,Philanthropic.Commitments
                                                        ))
#cleaning up zip codes removing -4 after 
dataclean$Zip <- gsub(dataclean$Zip, pattern="-.*", replacement = "")

#adding zip code data and column 
zip <- read.csv(here::here("final_project", "Salary_Zipcode.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")


#adding zip salary column
dataclean <-dataclean %>%
    mutate(zipcode_slry = VLOOKUP(Zip, zip, NAME, S1902_C03_002E))

#slry range 
dataclean <- dataclean %>%
  mutate(zipslry_range = 
    ifelse(zipcode_slry %in% 10000:89999, "90K-99K",
    ifelse(zipcode_slry %in% 90000:99999, "90K-99K",
    ifelse(zipcode_slry %in% 100000:149999, "100K-149K", 
    ifelse(zipcode_slry %in% 150000:199999, "150K-199K",
    ifelse(zipcode_slry %in% 200000:249999, "200K-249K",
    ifelse(zipcode_slry %in% 250000:299999, "250K-299K",
    ifelse(zipcode_slry %in% 300000:349999, "300K-349K",
    ifelse(zipcode_slry %in% 350000:399999, "350K-399K",
    ifelse(zipcode_slry %in% 400000:499999, "400K-499K",
    ifelse(zipcode_slry %in% 500000:999999, "500K-999K",
    NA)))))))))))

sum(is.na(dataclean$zipcode_slry))
[1] 62347
#adding scholarship data (y/n)
schlr <- read.csv(here::here("final_project", "scholarship.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")

#adding scholarship column
dataclean <-dataclean %>%
    mutate(scholarship = VLOOKUP(ID, schlr, ID, SCHOLARSHIP)) 

#replacing NA with 0 
 dataclean$scholarship <- replace_na(dataclean$scholarship,'0')
 
#replacing Y with 1 
dataclean$scholarship<-ifelse(dataclean$scholarship=="Y",1,0)

#checking how many are N
table(dataclean$scholarship)

     0      1 
295264  27962 
#checking and deleting scholarship column 
class(dataclean$schlr_fct)
[1] "NULL"
dataclean = subset(dataclean, select = -c(scholarship))
  
#checking for duplicates N >1 indicates a records values are in the file twice 
dataclean %>% group_by(ID) %>% count() %>% arrange(desc(n))

#removing duplicated records

dataclean <- unique(dataclean)

#n = 1 no ID with multiple records cleaned of dupes
dataclean %>% group_by(ID) %>% count() %>% arrange(desc(n))
NA

1d Creating many many factor variables


dataclean <- 
  dataclean %>% 
  #SEX
  mutate(sex_fct = 
           fct_explicit_na(Sex),
sex_simple = 
    fct_lump_n(Sex, n = 4),
#MARRIED
married_fct = 
           fct_explicit_na(Married),
  #DONOR SEGMENT
  donorseg_fct = 
           fct_explicit_na(Donor.Segment),
         donorseg_simple = 
           fct_lump_n(Donor.Segment, n = 4),
  #CONTACT RULE
         contact_fct = 
           fct_explicit_na(Contact.Rules),
         contact_simple = 
           fct_lump_n(Contact.Rules, n = 4),
  #SPOUSE MAIL
         spomail_fct = 
           fct_explicit_na(Spouse.Mail.Rules),
         spomail_simple = 
           fct_lump_n(Spouse.Mail.Rules, n = 4),
  #JOB TITLE
         jobtitle_fct = 
           fct_explicit_na(Job.Title),
         jobtitle_simple = 
           fct_lump_n(Job.Title, n = 5),
  #DEGREE TYPE 1
         deg1_fct = 
           fct_explicit_na(Degree.Type.1),
         deg1_simple = 
           fct_lump_n(Degree.Type.1, n = 5),
  #DEGREE TYPE 2
         deg2_fct = 
           fct_explicit_na(Degree.Type.2),
         deg2_simple = 
           fct_lump_n(Degree.Type.2, n = 5),
  #MAJOR 1
         maj1_fct = 
           fct_explicit_na(Major.1),
         maj1_simple = 
           fct_lump_n(Major.1, n = 5),
  #MAJOR 2
         maj2_fct = 
           fct_explicit_na(Major.2),
         maj2_simple = 
           fct_lump_n(Major.2, n = 5),
  #MINOR 1
         min1_fct = 
           fct_explicit_na(Minor.1),
         min1_simple = 
           fct_lump_n(Minor.1, n = 5),
  #MINOR 2
         min2_fct = 
           fct_explicit_na(Minor.2),
         min2_simple = 
           fct_lump_n(Minor.2, n = 5),
  #SCHOOL 1
         school1_fct = 
           fct_explicit_na(School.1),
         school1_simple = 
           fct_lump_n(School.1, n = 5),
  #SCHOOL 2
         school2_fct = 
           fct_explicit_na(School.2),
         school2_simple = 
           fct_lump_n(School.2, n = 5),
  #INSTITUTION TYPE
         insttype_fct = 
           fct_explicit_na(Institution.Type),
         insttype_simple = 
           fct_lump_n(Institution.Type, n = 5),
  #EXTRACURRICULAR
         extra_fct = 
           fct_explicit_na(Extracurricular),
         extra_simple = 
           fct_lump_n(Extracurricular, n = 5),
  #HH FIRST GIFT FUND
         hhfirstgift_fct = 
           fct_explicit_na(HH.First.Gift.Fund),
         hhfirstgift_simple = 
           fct_lump_n(HH.First.Gift.Fund, n = 5),
#CHILD 1 ENROLL STATUS
         ch1_enroll_fct = 
           fct_explicit_na(Child.1.Enroll.Status),
         ch1_enroll_simple = 
           fct_lump_n(Child.1.Enroll.Status, n = 4),
#CHILD 1 MAJOR
         ch1_maj_fct = 
           fct_explicit_na(Child.1.Major),
         ch1_maj_simple = 
           fct_lump_n(Child.1.Major, n = 4),
#CHILD 1 MINOR
         ch1_min_fct = 
           fct_explicit_na(Child.1.Minor),
         ch1_min_simple = 
           fct_lump_n(Child.1.Minor, n = 4),
#CHILD 1 SCHOOL
         ch1_school_fct = 
           fct_explicit_na(Child.1.School),
         ch1_school_simple = 
           fct_lump_n(Child.1.School, n = 4),
#CHILD 1 FEEDER
         ch1_feeder_fct = 
           fct_explicit_na(Child.1.Feeder.School),
         ch1_feeder_simple = 
           fct_lump_n(Child.1.Feeder.School, n = 4),
#CHILD 2 ENROLL STATUS
         ch1_enroll_fct = 
           fct_explicit_na(Child.2.Enroll.Status),
         ch2_enroll_simple = 
           fct_lump_n(Child.2.Enroll.Status, n = 4),
#CHILD 2 MAJOR
         ch2_maj_fct = 
           fct_explicit_na(Child.2.Major),
         ch2_maj_simple = 
           fct_lump_n(Child.2.Major, n = 4),
#CHILD 2 MINOR
         ch2_min_fct = 
           fct_explicit_na(Child.2.Minor),
         ch2_min_simple = 
           fct_lump_n(Child.2.Minor, n = 4),
#CHILD 2 SCHOOL
         ch2_school_fct = 
           fct_explicit_na(Child.2.School),
         ch2_school_simple = 
           fct_lump_n(Child.2.School, n = 4),
#CHILD 2 FEEDER
         ch2_feeder_fct = 
           fct_explicit_na(Child.2.Feeder.School),
         ch2_feeder_simple = 
           fct_lump_n(Child.2.Feeder.School, n = 4),
    )



#checking to see if its a factor
#class(dataclean$sex_fct)
#class(dataclean$donorseg_fct)
#class(dataclean$contact_fct)
#class(dataclean$spomail_fct)

#checking levels
#levels(dataclean$sex_simple)
#levels(dataclean$donorseg_simple)
#levels(dataclean$contact_simple)
#levels(dataclean$spomail_simple)
#levels(dataclean$hhfirstgift_simple)

#creating a table against Sex column 
#table(dataclean$sex_fct, dataclean$sex_simple)

Region Analysis

#grouping by region and analyzing 
dataclean %>%
  group_by(region) %>%
  summarise(Count = length(region),
            mean_total_giv = mean(HH.Lifetime.Giving)) %>%
  arrange(-Count) %>%
  filter(Count >= 100) %>%
  mutate(mean_total_giv = dollar(mean_total_giv)) %>%
  kable(col.names = c("Region", "Count", "Mean HH Lifetime Giving"), align=rep('c', 3)) %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Region Count Mean HH Lifetime Giving
So Cal 145139 $5,090.84
NA 130306 $2,040.98
Bay Area 20641 $755.92
Nor Cal 10707 $3,823.63
Seattle 5425 $922.08
New York 4959 $1,978.49
Portland 2976 $1,098.24
Denver 2847 $257.29
NA
NA

DonorSegment Analysis

#grouping by donorsegment and analyzing 
dataclean %>%
  group_by(Donor.Segment) %>%
  summarise(Count = length(Donor.Segment),
            mean_total_giv = mean(HH.Lifetime.Giving)) %>%
  arrange(-Count) %>%
  filter(Count >= 100) %>%
  #added scales package to have the values show in dollar 
  mutate(mean_total_giv = (dollar(mean_total_giv))) %>%
  kable(col.names = c("Donor Segment", "Count", "Mean HH Lifetime Giving"), align=rep('c', 3)) %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Donor Segment Count Mean HH Lifetime Giving
NA 231974 $0.00
Lost Donor 69718 $4,954.47
Lapsed Donor 11193 $10,069.79
Current Donor 5603 $90,638.32
Lapsing Donor 3862 $16,590.15
At-Risk Donor 650 $77,143.93
NA
NA

First gift size

aq <- quantile(dataclean$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)

aq <- as.data.frame(aq)

aq$aq <- dollar(aq$aq)

aq %>%
  kable(col.names = "Quantile") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Quantile
25% $0.00
50% $0.00
75% $0.00
90% $40.00
99% $1,910.06
NA
NA

Consecutive giving

#consecutive years of giving 
dataclean %>%
  filter(Max.Consec.Fiscal.Years > 0) %>%
  ggplot(aes(Max.Consec.Fiscal.Years)) + geom_histogram(fill = "#002845", bins = 20) + 
  theme_economist_white() +
  ggtitle("Consecutive Years of Giving Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,2)) +
  scale_y_continuous(breaks = seq(0,10000000,5000)) 

Lifetime giving based on number of children

dataclean %>%
  filter(HH.Lifetime.Giving <= 10000) %>%
  filter(HH.Lifetime.Giving > 0) %>%
  mutate(`No_of_Children` = as.factor(`No_of_Children`)) %>%
  ggplot(aes(HH.Lifetime.Giving, fill = `No_of_Children`)) + geom_histogram(bins = 30) + theme_economist_white() +
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,100000,1000)) +
  scale_y_continuous(breaks = seq(0,100000000,5000)) +
  ggtitle("Giving distribution and number of children")+ 
  scale_fill_manual(values=c("#002845", "#00cfcc", "#ff9973"))

Mean, Median, and Count of Giving in Age Ranges


age_range_giving <- dataclean %>%
  group_by(age_range) %>%
  summarise(avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
            med_giving = median(HH.Lifetime.Giving, na.rm = TRUE),
            amount_of_people_in_age_range = n())


glimpse(age_range_giving)

Part 2

2a) Plotting average giving by age range


age_range_giving <-
  age_range_giving %>%
  mutate(age_range = factor(age_range))

ggplot(age_range_giving, aes(age_range, avg_giving)) +
  geom_bar(stat = "identity")+
  theme(axis.text.x = element_text(angle=45,
                                        hjust=1))

2b) Count of donors based on age range (another way to look at it)


ggplot(dataclean, 
       aes(age_range)) + 
       geom_bar() + 
       theme(axis.text.x = element_text(angle=45,
                                        hjust=1)) + 
  labs(title = "Count of Age Ranges", x = "", y = "")
  

2c) Boxplot of the Age Ranges Against the Lifetime Giving Amounts with a log scale applied - the reason we applied log scale is to resolve issues with visualizations that skew towards large values in our dataset.


ggplot(dataclean, aes(age_range,HH.Lifetime.Giving,fill = age_range)) + 
  geom_boxplot(
  outlier.colour = "red") + 
  scale_y_log10() +
  theme(axis.text.x=element_text(angle=45,hjust=1))
  

2d) Splitting by age and gender



#creating boxplots 
dataclean %>% 
  filter(Age < 100) %>% #removing the weird outliers that are over 100 
  filter(Sex %in% c("M", "F")) %>%
  ggplot(aes(Sex, Age)) + 
  geom_boxplot() + 
  theme_economist() + 
  ggtitle("Ages of Donors Based on Gender") + 
  xlab(NULL) + ylab(NULL)
  

Giving by gender


#remove NAs U X

dataclean2 <- dataclean %>%
  filter(Sex %in% c("M", "F")) 

q <- ggplot(dataclean2) 
q + stat_summary_bin(
  aes(y = HH.Lifetime.Giving, x = Sex), 
  fun.y = "mean", geom = "bar") 
  
summary(dataclean$sex_simple)

Mean age by gender

#breakdown of sexs 
tally(group_by(dataclean, Sex))

summarize(group_by(dataclean, Sex), 
          avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
          avg_age = mean(Age, na.rm = TRUE),
          med_age = median(Age, na.rm = TRUE))

#grouping by sex and age range for slides 
tally(group_by(dataclean, Sex, age_range))

2e) Distribution of people in the states that they live.


  dataclean %>%
  mutate(State = ifelse(State == " ", "NA", State)) %>%
  filter(State != "NA") %>%
  group_by(State) %>%
  summarise(Count = length(State)) %>%
  filter(Count > 800) %>%
  arrange(-Count) %>%
  kable(col.names = c("Donor's State", "Count")) %>%
  kable_styling(bootstrap_options = c("condensed"),
                full_width = F)
  
 
  
  

2f) Looking at all donors first gift amount. 75% made a first gift of <100.


 no_non_donors <- dataclean %>%
  filter(Lifetime.Giving != 0)
  
nd <- quantile(no_non_donors$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)

nd <- as.data.frame(nd)

nd %>%
  kable(col.names = "Quantile") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
  
  

Modeling for you

Split data





#converting married Y and N to 1 and 0 
dataclean <- dataclean %>%
      mutate(Married_simple = ifelse(Married == "N",0,1))


dataclean <- dataclean %>%
  mutate(hh.lifetime.giving_fct = as.factor(HH.Lifetime.Giving)) %>%
  mutate(HH.Lifetime.Giving.Plus = log(HH.Lifetime.Giving + 1))


library("rsample")

data_split <- initial_split(dataclean, prop = 0.75)

data_train <- training(data_split)
data_test <- testing(data_split)
p <- dataclean %>%
  ggplot(aes(Age)) + geom_histogram(bins=30, fill = "blue") + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(5,100,by = 20)) +
  scale_y_continuous(breaks = seq(20,100,by = 20)) + xlim(c(20,100))

ggplotly(p)
  
p

ggplot(data = dataclean, aes(x = Age)) + geom_histogram(fill ="blue")+ xlim(c(20,100))

  

Another Histogram


dataclean %>%
  filter(Age >= 10) %>%
  filter(Age <= 90) %>%
  ggplot(aes(Age)) + geom_histogram(fill = "#002845", bins = 20) + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,5)) +
  scale_y_continuous(breaks = seq(0,10000000,2000)) 

Age distribution by gender

#Age Gender filtered out below 15 and above 90 - also removed U X the weird values 
dataclean %>%
  filter(Age >= 15) %>%
  filter(Age <= 90) %>%
  mutate(Sex = as.factor(Sex)) %>%
  filter(Sex != "U") %>%
  filter(Sex != "X") %>%
  ggplot(aes(Age, fill = Sex)) + geom_histogram(bins = 25) + theme_economist_white() +
  ggtitle("Age Distribution by Gender") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,10)) +
  scale_y_continuous(breaks = seq(0,50000,2000)) + scale_fill_manual(values=c("#ff9973", "#00cfcc"))

Donor age distribution by marital status

#Age Marital Status
dataclean %>%
  filter(Age >= 20) %>%
  filter(Age <= 85) %>%
  ggplot(aes(Age, fill = Married)) + geom_histogram(bins = 25) + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution by Marital Status") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,5)) +
  scale_y_continuous(breaks = seq(0,50000,2000)) + scale_fill_manual(values=c("#ff9973", "#00cfcc"))

Linear Model

#These will focus on predicting whether a constituent is a donor or non-donor. 


mod1lm <- lm( Lifetime.Giving ~ Married_simple,
           data = data_train)

mod2lm <- lm( Total.Giving.Years ~ Lifetime.Giving,
           data = data_train)

mod3lm <- lm( Lifetime.Giving ~ region,
           data = data_train)

summary(mod1lm)

Call:
lm(formula = Lifetime.Giving ~ Married_simple, data = data_train)

Residuals:
     Min       1Q   Median       3Q      Max 
   -2867    -2742    -2661    -2661 18111464 

Coefficients:
               Estimate Std. Error t value            Pr(>|t|)    
(Intercept)      2660.9      251.3  10.588 <0.0000000000000002 ***
Married_simple    205.9      469.1   0.439               0.661    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 104400 on 242248 degrees of freedom
Multiple R-squared:  7.953e-07, Adjusted R-squared:  -3.333e-06 
F-statistic: 0.1927 on 1 and 242248 DF,  p-value: 0.6607
summary(mod2lm)

Call:
lm(formula = Total.Giving.Years ~ Lifetime.Giving, data = data_train)

Residuals:
    Min      1Q  Median      3Q     Max 
-36.600  -0.554  -0.554  -0.554  39.403 

Coefficients:
                     Estimate    Std. Error t value
(Intercept)     0.55445026328 0.00396511550  139.83
Lifetime.Giving 0.00000343205 0.00000003795   90.43
                           Pr(>|t|)    
(Intercept)     <0.0000000000000002 ***
Lifetime.Giving <0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.951 on 242248 degrees of freedom
Multiple R-squared:  0.03266,   Adjusted R-squared:  0.03265 
F-statistic:  8178 on 1 and 242248 DF,  p-value: < 0.00000000000000022
summary(mod3lm)

Call:
lm(formula = Lifetime.Giving ~ region, data = data_train)

Residuals:
     Min       1Q   Median       3Q      Max 
   -3977    -3968    -3968    -3598 18110156 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)       513.0      950.9   0.539 0.589558    
regionDenver     -367.7     2739.3  -0.134 0.893220    
regionNew York   1954.2     2160.7   0.904 0.365769    
regionNor Cal    3464.0     1623.1   2.134 0.032826 *  
regionPortland    161.0     2680.2   0.060 0.952111    
regionSeattle    -128.2     2088.2  -0.061 0.951057    
regionSo Cal     3455.5     1016.1   3.401 0.000672 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 118200 on 144684 degrees of freedom
  (97559 observations deleted due to missingness)
Multiple R-squared:  0.0001214, Adjusted R-squared:  7.989e-05 
F-statistic: 2.927 on 6 and 144684 DF,  p-value: 0.007435
#increasing the giving year one year increase total giving by 0.0035


ggplot(data = data_train, aes(x = Age, y = log(HH.Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~region) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Region")
`geom_smooth()` using formula 'y ~ x'

ggplot(data = data_train, aes(x = Age, y = log(HH.Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~nmb_degree) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Number of Degrees")
`geom_smooth()` using formula 'y ~ x'

ggplot(data = data_train, aes(x = Age, y = log(HH.First.Gift.Amount))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~donorseg_fct) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Donor Segment")
`geom_smooth()` using formula 'y ~ x'

#This plot actually has some interesting results
ggplot(data = data_train, aes(x = Age, y = log(Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~No_of_Children) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("# Children")
`geom_smooth()` using formula 'y ~ x'

data_train %>% 
  select_if(is.factor) %>% 
  glimpse()
Rows: 242,250
Columns: 54
$ major_gifter           <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ sex_fct                <fct> F, M, F, M, (Missing), M, M, (Missing…
$ sex_simple             <fct> F, M, F, M, NA, M, M, NA, M, NA, M, M…
$ married_fct            <fct> Y, Y, N, N, N, N, N, N, N, N, N, N, N…
$ donorseg_fct           <fct> Lost Donor, (Missing), (Missing), (Mi…
$ donorseg_simple        <fct> Lost Donor, NA, NA, NA, NA, Lost Dono…
$ contact_fct            <fct> No Solicitations, (Missing), (Missing…
$ contact_simple         <fct> No Solicitations, NA, NA, NA, NA, No …
$ spomail_fct            <fct> No Solicitations, (Missing), (Missing…
$ spomail_simple         <fct> No Solicitations, NA, NA, NA, NA, NA,…
$ jobtitle_fct           <fct> (Missing), Manager, (Missing), Public…
$ jobtitle_simple        <fct> NA, Other, NA, Other, NA, NA, NA, NA,…
$ deg1_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ deg1_simple            <fct> NA, NA, NA, NA, NA, Bachelor of Arts,…
$ deg2_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ deg2_simple            <fct> NA, NA, NA, NA, NA, Master of Arts, N…
$ maj1_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ maj1_simple            <fct> NA, NA, NA, NA, NA, Other, Law (Full-…
$ maj2_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ maj2_simple            <fct> NA, NA, NA, NA, NA, Other, NA, NA, NA…
$ min1_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ min1_simple            <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ min2_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ min2_simple            <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ school1_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ school1_simple         <fct> NA, NA, NA, NA, NA, NA, Other, NA, NA…
$ school2_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ school2_simple         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ insttype_fct           <fct> (Missing), (Missing), (Missing), (Mis…
$ insttype_simple        <fct> NA, NA, NA, NA, NA, NA, Law JD Full-T…
$ extra_fct              <fct> (Missing), (Missing), (Missing), (Mis…
$ extra_simple           <fct> NA, NA, NA, NA, NA, Other, NA, NA, NA…
$ hhfirstgift_fct        <fct> (Missing), (Missing), (Missing), (Mis…
$ hhfirstgift_simple     <fct> NA, NA, NA, NA, NA, Pre-SRN Conversio…
$ ch1_enroll_fct         <fct> (Missing), Program Completed, (Missin…
$ ch1_enroll_simple      <fct> NA, NA, NA, NA, NA, Program Completed…
$ ch1_maj_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ ch1_maj_simple         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ch1_min_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ ch1_min_simple         <fct> NA, NA, NA, NA, NA, Non-Degree: GR Ta…
$ ch1_school_fct         <fct> (Missing), (Missing), (Missing), (Mis…
$ ch1_school_simple      <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ch1_feeder_fct         <fct> (Missing), Palm Beach State College, …
$ ch1_feeder_simple      <fct> NA, Other, NA, NA, NA, NA, NA, NA, NA…
$ ch2_enroll_simple      <fct> NA, Program Completed, NA, NA, NA, NA…
$ ch2_maj_fct            <fct> (Missing), Business Administration BS…
$ ch2_maj_simple         <fct> NA, Business Administration BS, NA, N…
$ ch2_min_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ ch2_min_simple         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ch2_school_fct         <fct> (Missing), George L. Argyros School o…
$ ch2_school_simple      <fct> NA, George L. Argyros School of Busin…
$ ch2_feeder_fct         <fct> (Missing), (Missing), (Missing), (Mis…
$ ch2_feeder_simple      <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ hh.lifetime.giving_fct <fct> 25, 0, 0, 0, 0, 8048.75, 0, 0, 0, 0, …

MORE MODELS

Big logistic model

print(calc_auc(p_train)$AUC)
[1] 0.8930325
print(calc_auc(p_test)$AUC)
[1] 0.8773735

RIDGE


library('glmnet')
library('glmnetUtils')

ridge_fit1 <- cv.glmnet(HH.Lifetime.Giving.Plus ~ sex_fct + donorseg_fct + No_of_Children,
                       data = data_train,
                       alpha = 0)

#Alpha 0 sets the Ridge
print(ridge_fit1)
Call:
cv.glmnet.formula(formula = HH.Lifetime.Giving.Plus ~ sex_fct + 
    donorseg_fct + No_of_Children, data = data_train, alpha = 0)

Model fitting options:
    Sparse model matrix: FALSE
    Use model.frame: FALSE
    Number of crossvalidation folds: 10
    Alpha: 0
    Deviance-minimizing lambda: 0.2202558  (+1 SE): 0.2652989
print(ridge_fit1$lambda.min)
[1] 0.2202558
print(ridge_fit1$lambda.1se)
[1] 0.2652989
plot(ridge_fit1)

LASSO

coef(lasso_fit)
37 x 1 sparse Matrix of class "dgCMatrix"
                                                                          s1
(Intercept)                                                       4.54735146
sex_fctF                                                         -0.07604783
sex_fctM                                                          .         
sex_fctU                                                          .         
sex_fctX                                                          .         
sex_fct(Missing)                                                  .         
jobtitle_simpleAttorney                                           .         
jobtitle_simpleOwner                                              .         
jobtitle_simplePresident                                          0.20429193
jobtitle_simpleTeacher                                            .         
jobtitle_simpleUnknown Position                                   .         
jobtitle_simpleOther                                              .         
nmb_degree                                                        .         
school1_simpleCollege of Health and Behavioral Sciences           .         
school1_simpleDonna Ford Attallah College of Educational Studies  .         
school1_simpleGeorge L. Argyros School of Business and Economics  0.09311429
school1_simpleLawrence and Kristina Dodge Coll of Film & Media   -0.36459784
school1_simpleWilkinson Coll of Arts  Humanities  & Soc Sciences  .         
school1_simpleOther                                               .         
hhfirstgift_simpleChapman Annual Scholarship Fund                 .         
hhfirstgift_simpleChapman Fund                                   -0.65373923
hhfirstgift_simpleJog-A-Thon                                      .         
hhfirstgift_simplePhonathon                                       .         
hhfirstgift_simplePre-SRN Conversion Gift History                 0.42389757
hhfirstgift_simpleOther                                           .         
maj1_simpleBusiness Administration BS                             .         
maj1_simpleEducation                                              .         
maj1_simpleLaw (Full-Time)                                        .         
maj1_simpleUndecided - UG                                         .         
maj1_simpleUnknown Major                                          .         
maj1_simpleOther                                                  .         
donorseg_simpleAt-Risk Donor                                      0.90437087
donorseg_simpleCurrent Donor                                      2.22091067
donorseg_simpleLapsed Donor                                       .         
donorseg_simpleLapsing Donor                                      0.79860122
donorseg_simpleLost Donor                                        -0.20881610
No_of_Children                                                    0.76764765
#Default setting is lambda.1se

#From the book - showing convergence with lambda values
plot(lasso_fit$glmnet.fit, xvar="lambda")


enet_mod <- cva.glmnet(HH.Lifetime.Giving.Plus ~ sex_fct + jobtitle_simple + nmb_degree + school1_simple + hhfirstgift_simple + maj1_simple + donorseg_simple + No_of_Children + Married,
                       data = data_train,
                       alpha = seq(0,1, by = 0.1))

print(enet_mod)
Call:
cva.glmnet.formula(formula = HH.Lifetime.Giving.Plus ~ sex_fct + 
    jobtitle_simple + nmb_degree + school1_simple + hhfirstgift_simple + 
    maj1_simple + donorseg_simple + No_of_Children + Married, 
    data = data_train, alpha = seq(0, 1, by = 0.1))

Model fitting options:
    Sparse model matrix: FALSE
    Use model.frame: FALSE
    Alpha values: 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
    Number of crossvalidation folds for lambda: 10
plot(enet_mod)

NA
NA

ELASTICNET


minlossplot(enet_mod, 
            cv.type = "min")

get_alpha <- function(fit) {
  alpha <- fit$alpha
  error <- sapply(fit$modlist, 
                  function(mod) {min(mod$cvm)})
  alpha[which.min(error)]
}

get_model_params <- function(fit) {
  alpha <- fit$alpha
  lambdaMin <- sapply(fit$modlist, `[[`, "lambda.min")
  lambdaSE <- sapply(fit$modlist, `[[`, "lambda.1se")
  error <- sapply(fit$modlist, function(mod) {min(mod$cvm)})
  best <- which.min(error)
  data.frame(alpha = alpha[best], lambdaMin = lambdaMin[best],
             lambdaSE = lambdaSE[best], eror = error[best])
}

best_alpha <- get_alpha(enet_mod)
print(best_alpha)
[1] 0
get_model_params(enet_mod)

best_mod <- enet_mod$modlist[[which(enet_mod$alpha == best_alpha)]]

print(best_mod)

Call:  glmnet::cv.glmnet(x = x, y = y, weights = ..1, offset = ..2,      nfolds = nfolds, foldid = foldid, alpha = a) 

Measure: Mean-Squared Error 

    Lambda Index Measure      SE Nonzero
min 0.0814   100   2.596 0.08240      32
1se 1.0041    73   2.669 0.08313      32

Ridges plot - could be useful for plotting donations vs donor segment

ggplot(data_train, aes(x = HH.Lifetime.Giving, y = region)) + geom_density_ridges(rel_min_height = 0.005) + xlim(c(25000, 100000)) + 
      ggtitle("HH Lifetime Giving by Donor Segment")
Picking joint bandwidth of 8480


library('corrplot')

#removing ID zip and nonnumeric 
corrplot_data <- dataclean[-c(1:48,52:56,58:60,63,66:67,70:72,74:81,83:132)]

#Convert from character to numeric data type
convert_fac2num <- function(x){
  as.numeric(as.factor(x))
}

corrplot_data <- mutate_at(corrplot_data,
                     .vars = c(1:12),
                     .funs = convert_fac2num)
#making a matrix
cd_cor <- cor(corrplot_data)

#creating correlation
col <- colorRampPalette(c("#BB4400", "#EE9990", 
  "#FFFFFF", "#77AAEE", "#4477BB"))
corrplot(cd_cor, method="color", col=col(100),
  type="lower", addCoef.col = "black",
  tl.pos="lt", tl.col="black", 
  tl.cex=0.7, tl.srt=45, 
  number.cex=0.7,
  diag=FALSE)

#correlation matrix
# pairs(~Age + Months.Since.Last.Gift + donorseg_fct + 
#     nmb_degree + No_of_Children + HH.First.Gift.Age + HH.First.Gift.Amount + Total.Giving.Years,
#     col = corrplot_data$HH.Lifetime.Giving,
#     data = corrplot_data, 
#     main = "Donor Scatter Plot Matrix")

#worthless.. 

ggplot(data = corrplot_data, aes(x = nmb_degree, y = HH.Lifetime.Giving)) + 
  geom_point(aplha = 1/10)+
  geom_smooth(method = "lm", color ="red") 

Random Forest


library('randomForest')

rf_fit_donor <- randomForest(Lifetime.Giving ~ ., 
                       data = data_train,
                       type = classification,
                       mtry = 7,
                       na.action = na.roughfix,
                       ntree = 200,
                       importance=TRUE
                       )

print(rf_fit_donor)

varImpPlot(rf_fit_donor, sort = TRUE, 
           n.var = 5,
           type = 2, class = NULL, scale = TRUE, 
           main = deparse(substitute(rf_fit_donor)))

library('randomForestExplainer')

plot_min_depth_distribution(
  rf_fit_donor,
  k = 10,
  min_no_of_trees = 0,
  mean_sample = "top_trees",
  mean_scale = FALSE,
  mean_round = 2,
  main = "Distribution of minimal depth and its mean"
)
#Splitting Category out to check if the category is useful for analysis
data_category_split_out <- dataclean %>%
  mutate(Category.Codes = trim(strsplit(as.character(Category.Codes), "|", fixed = TRUE))) %>%
  unnest(Category.Codes) %>% pivot_wider(names_from = Category.Codes,values_from =Category.Codes, values_fn = length)
LS0tCnRpdGxlOiAiQlJPQ09ERSBTdW1tYXJ5IFN0YXRpc3RpY3MiCmF1dGhvcjogIkFhcm9uIFdpbGxpcywgQ2Fubm9uIEJyb29rZSwgSm9zaHVhIEhlbmRlcnNvbiwgUnlhbiBSYWRjbGlmZiIKc3VidGl0bGU6ICJCVVM2OTYgRmluYWwgUHJvamVjdCB2MTQuMzMzIFJlcGVhdGluZyBvZiBjb3Vyc2UiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQoKIyBQbGVhc2UgbGVhdmUgdGhpcyBjb2RlIGNodW5rIGFzIGlzLiBJdCBtYWtlcyBzb21lIHNsaWdodCBmb3JtYXR0aW5nIGNoYW5nZXMgdG8gYWx0ZXIgdGhlIG91dHB1dCB0byBiZSBtb3JlIGFlc3RoZXRpY2FsbHkgcGxlYXNpbmcuIAoKbGlicmFyeSgna25pdHInKQoKCiMgQ2hhbmdlIHRoZSBudW1iZXIgaW4gc2V0IHNlZWQgdG8geW91ciBvd24gZmF2b3JpdGUgbnVtYmVyCnNldC5zZWVkKDE4MTgpCm9wdGlvbnMod2lkdGg9NzApCm9wdGlvbnMoc2NpcGVuPTk5KQoKCiMgdGhpcyBzZXRzIHRleHQgb3V0cHV0dGVkIGluIGNvZGUgY2h1bmtzIHRvIHNtYWxsCm9wdHNfY2h1bmskc2V0KHRpZHkub3B0cz1saXN0KHdpZHRoLndyYXA9NTApLHRpZHk9VFJVRSwgc2l6ZSA9ICJ2c21hbGwiKSAgCm9wdHNfY2h1bmskc2V0KG1lc3NhZ2UgPSBGQUxTRSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLAogICAgICAgICAgICAgICAjICJjYWNoaW5nIiBzdG9yZXMgb2JqZWN0cyBpbiBjb2RlIGNodW5rcyBhbmQgb25seSByZXdyaXRlcyBpZiB5b3UgY2hhbmdlIHRoaW5ncwogICAgICAgICAgICAgICBjYWNoZSA9IFRSVUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAjIGF1dG9tYXRpY2FsbHkgZG93bmxvYWRzIGRlcGVuZGVuY3kgZmlsZXMKICAgICAgICAgICAgICAgYXV0b2RlcCA9IFRSVUUsCiAgICAgICAgICAgICAgICMgCiAgICAgICAgICAgICAgIGNhY2hlLmNvbW1lbnRzID0gRkFMU0UsCiAgICAgICAgICAgICAgICMgCiAgICAgICAgICAgICAgIGNvbGxhcHNlID0gVFJVRSwKICAgICAgICAgICAgICAgIyBjaGFuZ2UgZmlnLndpZHRoIGFuZCBmaWcuaGVpZ2h0IHRvIGNoYW5nZSB0aGUgY29kZSBoZWlnaHQgYW5kIHdpZHRoIGJ5IGRlZmF1bHQKICAgICAgICAgICAgICAgZmlnLndpZHRoID0gNS41LCAgCiAgICAgICAgICAgICAgIGZpZy5oZWlnaHQgPSA0LjUsCiAgICAgICAgICAgICAgIGZpZy5hbGlnbj0nY2VudGVyJykKCgpgYGAKCmBgYHtyIHNldHVwLTJ9CgojIEFsd2F5cyBwcmludCB0aGlzIG91dCBiZWZvcmUgeW91ciBhc3NpZ25tZW50CnNlc3Npb25JbmZvKCkKZ2V0d2QoKQoKYGBgCgoKPCEtLSAjIyMgc3RhcnQgYW5zd2VyaW5nIHlvdXIgcHJvYmxlbSBzZXQgaGVyZSAtLT4KPCEtLSBZb3UgbWF5IGV4cG9ydCB5b3VyIGhvbWV3b3JrIGluIGVpdGhlciBodG1sIG9yIHBkZiwgd2l0aCB0aGUgZm9ybWVyIHVzdWFsbHkgYmVpbmcgZWFzaWVyLiAKICAgICBUbyBleHBvcnQgb3IgY29tcGlsZSB5b3VyIFJtZCBmaWxlOiBjbGljayBhYm92ZSBvbiAnS25pdCcgdGhlbiAnS25pdCB0byBIVE1MJyAtLT4KPCEtLSBCZSBzdXJlIHRvIHN1Ym1pdCBib3RoIHlvdXIgLlJtZCBmaWxlIGFuZCB0aGUgY29tcGlsZWQgLmh0bWwgb3IgLnBkZiBmaWxlIGZvciBmdWxsIGNyZWRpdCAtLT4KCgpgYGB7ciBzZXR1cC0zfQoKIyBsb2FkIGFsbCB5b3VyIGxpYnJhcmllcyBpbiB0aGlzIGNodW5rIApsaWJyYXJ5KCd0aWR5dmVyc2UnKQpsaWJyYXJ5KCJmcyIpCmxpYnJhcnkoJ2hlcmUnKQpsaWJyYXJ5KCdkcGx5cicpCmxpYnJhcnkoJ3RpZHl2ZXJzZScpCmxpYnJhcnkoJ2dncGxvdDInKQpsaWJyYXJ5KCdnZ3JlcGVsJykKbGlicmFyeSgnZ2d0aGVtZXMnKQpsaWJyYXJ5KCdmb3JjYXRzJykKbGlicmFyeSgncnNhbXBsZScpCmxpYnJhcnkoJ2x1YnJpZGF0ZScpCmxpYnJhcnkoJ2dndGhlbWVzJykKbGlicmFyeSgna2FibGVFeHRyYScpCmxpYnJhcnkoJ3Bhc3RlY3MnKQpsaWJyYXJ5KCd2aXJpZGlzJykKbGlicmFyeSgncGxvdGx5JykKbGlicmFyeSgndGlkeXF1YW50JykKbGlicmFyeSgnc2NhbGVzJykKbGlicmFyeSgiZ2RhdGEiKQoKIyBub3RlLCBkbyBub3QgcnVuIGluc3RhbGwucGFja2FnZXMoKSBpbnNpZGUgYSBjb2RlIGNodW5rLiBpbnN0YWxsIHRoZW0gaW4gdGhlIGNvbnNvbGUgb3V0c2lkZSBvZiBhIGNvZGUgY2h1bmsuIAoKYGBgCgoKCiMjIFBhcnQgMSAtIEZpbmFsIFByb2plY3QgQ2xlYW5pbmcgYW5kIFN1bW1hcnkgU3RhdGlzdGljcyAKCjFhKSBMb2FkaW5nIGRhdGEKCmBgYHtyfQoKI1JlYWRpbmcgdGhlIGRhdGEgaW4gYW5kIGRvaW5nIG1pbm9yIGluaXRpYWwgY2xlYW5pbmcgaW4gdGhlIGZ1bmN0aW9uIGNhbGwKI1JlcHJvZHVjaWJsZSBkYXRhIGFuYWx5c2lzIHNob3VsZCBhdm9pZCBhbGwgYXV0b21hdGljIHN0cmluZyB0byBmYWN0b3IgY29udmVyc2lvbnMuCiNzdHJpcC53aGl0ZSByZW1vdmVzIHdoaXRlIHNwYWNlIAojbmEuc3RyaW5ncyBpcyBhIHN1YnN0aXR1dGlvbiBzbyBhbGwgdGhhdCBoYXZlICIiIHdpbGwgPSBuYQpkYXRhIDwtIHJlYWQuY3N2KGhlcmU6OmhlcmUoImZpbmFsX3Byb2plY3QiLCAiZG9ub3JfZGF0YS5jc3YiKSwKICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgc3RyaXAud2hpdGUgPSBUUlVFLAogICAgICAgICAgICAgICAgIG5hLnN0cmluZ3MgPSAiIikKCgoKYGBgCgoKMWIpIEZpeGluZyB0aGUgd29ua3kgRE9CICYgRGF0YSBjbGVhbnVwCgpgYGB7cn0KCiMoQmlydGhkYXRlIGFuZCBBZ2UsIElEIGFzIGEgbnVtYmVyKWFkZGluZyBET0IgKEFnZS9TcG91c2UgQWdlKSBpbiB5ZWFycyBjb2x1bW5zIGFuZCBhZGRpbmcgdHdvIGZpZWxkcyBmb3IgYXNzaWdubWVudCBhbmQgbnVtYmVyIG9mIGNoaWxkcmVuIGFuZCBudW1iZXIgb2YgZGVncmVlcwpkYXRhY2xlYW4gPC0gZGF0YSAlPiUKICBtdXRhdGUoQmlydGhkYXRlID0gaWZlbHNlKEJpcnRoZGF0ZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBCaXJ0aGRhdGUpKSAlPiUKICBtdXRhdGUoQmlydGhkYXRlID0gbWR5KEJpcnRoZGF0ZSkpICU+JQogIG11dGF0ZShBZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBCaXJ0aGRhdGUsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lCiAgbXV0YXRlKFNwb3VzZS5CaXJ0aGRhdGUgPSBpZmVsc2UoU3BvdXNlLkJpcnRoZGF0ZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBTcG91c2UuQmlydGhkYXRlKSkgJT4lCiAgbXV0YXRlKFNwb3VzZS5CaXJ0aGRhdGUgPSBtZHkoU3BvdXNlLkJpcnRoZGF0ZSkpICU+JQogIG11dGF0ZShTcG91c2UuQWdlID0gYXMubnVtZXJpYyhmbG9vcihpbnRlcnZhbChzdGFydD0gU3BvdXNlLkJpcnRoZGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUKICBtdXRhdGUoSUQgPSBhcy5udW1lcmljKElEKSkgJT4lIAogIG11dGF0ZShBc3NpZ25tZW50X2ZsYWcgPSBpZmVsc2UoaXMubmEoQXNzaWdubWVudC5OdW1iZXIpLCAwLDEpKSAlPiUgCiAgbXV0YXRlKCBOb19vZl9DaGlsZHJlbiA9IGlmZWxzZShpcy5uYShDaGlsZC4xLklEKSwwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGlzLm5hKENoaWxkLjIuSUQpLDEsMikpKSU+JQogbXV0YXRlKElEID0gYXMubnVtZXJpYyhJRCkpICU+JSAKICAgIG11dGF0ZSggbm1iX2RlZ3JlZSA9IGlmZWxzZShpcy5uYShEZWdyZWUuVHlwZS4xKSwwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGlzLm5hKERlZ3JlZS5UeXBlLjIpLDEsMikpKQojY29uZmVycmFsIGRhdGVzCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjEgPSBpZmVsc2UoQ29uZmVycmFsLkRhdGUuMSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBDb25mZXJyYWwuRGF0ZS4xKSkgJT4lCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjEgPSBtZHkoQ29uZmVycmFsLkRhdGUuMSkpICU+JQogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4xLkFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IENvbmZlcnJhbC5EYXRlLjEsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lCiAgCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjIgPSBpZmVsc2UoQ29uZmVycmFsLkRhdGUuMiA9PSAiMDAwMS0wMS0wMSIsIE5BLCBDb25mZXJyYWwuRGF0ZS4yKSkgJT4lCiAgbXV0YXRlKENvbmZlcnJhbC5EYXRlLjIgPSBtZHkoQ29uZmVycmFsLkRhdGUuMikpICU+JQogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4yLkFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IENvbmZlcnJhbC5EYXRlLjIsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lCiAgCiAgbXV0YXRlKExhc3QuQ29udGFjdC5CeS5BbnlvbmUgPSBpZmVsc2UoTGFzdC5Db250YWN0LkJ5LkFueW9uZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBMYXN0LkNvbnRhY3QuQnkuQW55b25lKSkgJT4lCiAgbXV0YXRlKExhc3QuQ29udGFjdC5CeS5BbnlvbmUgPSBtZHkoTGFzdC5Db250YWN0LkJ5LkFueW9uZSkpICU+JQogIG11dGF0ZShMYXN0LkNvbnRhY3QuQWdlID0gYXMubnVtZXJpYyhmbG9vcihpbnRlcnZhbChzdGFydD0gTGFzdC5Db250YWN0LkJ5LkFueW9uZSwgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUKICAKIG11dGF0ZShISC5GaXJzdC5HaWZ0LkRhdGUgPSBpZmVsc2UoSEguRmlyc3QuR2lmdC5EYXRlID09ICIwMDAxLTAxLTAxIiwgTkEsIEhILkZpcnN0LkdpZnQuRGF0ZSkpICU+JQogIG11dGF0ZShISC5GaXJzdC5HaWZ0LkRhdGUgPSBtZHkoSEguRmlyc3QuR2lmdC5EYXRlKSkgJT4lCm11dGF0ZShISC5GaXJzdC5HaWZ0LkFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IEhILkZpcnN0LkdpZnQuRGF0ZSwgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKQoKI21ham9yIGdpZnQgCmRhdGFjbGVhbiA8LSAKICBkYXRhY2xlYW4gJT4lIAogIG11dGF0ZShtYWpvcl9naWZ0ZXIgPSBpZmVsc2UoTGlmZXRpbWUuR2l2aW5nID4gNTAwMDAsIDEsMCkgJT4lIGZhY3RvciguLCBsZXZlbHMgPSBjKCIwIiwiMSIpKSkKCgojc3BsaXR0aW5nIHVwIHRoZSBhZ2UgaW50byByYW5nZXMgYW5kIGNyZWF0aW5nIGNhdGVnb3J5IGZvciBlYXN5IHZpc3VhbGl6YXRpb24gCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKGFnZV9yYW5nZSA9IAogICAgaWZlbHNlKEFnZSAlaW4lIDEwOjE5LCAiMTAgPCAyMCB5ZWFycyBvbGQiLAogICAgaWZlbHNlKEFnZSAlaW4lIDIwOjI5LCAiMjAgPCAzMCB5ZWFycyBvbGQiLCAKICAgIGlmZWxzZShBZ2UgJWluJSAzMDozOSwgIjMwIDwgNDAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA0MDo0OSwgIjQwIDwgNTAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA1MDo1OSwgIjUwIDwgNjAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA2MDo2OSwgIjYwIDwgNzAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA3MDo3OSwgIjcwIDwgODAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA4MDo4OSwgIjgwIDwgOTAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA5MDoxMjAsICI5MCsgeWVhcnMgb2xkIiwKICAgIE5BKSkpKSkpKSkpKQoKCiNzZWVpbmcgd2hhdCB3ZSBoYXZlCnRhYmxlKGRhdGFjbGVhbiRhZ2VfcmFuZ2UpCiM1MC02MCBpcyB0aGUgbW9zdCBjb21tb24gYWdlIHJhbmdlIAoKI2NyZWF0aW5nIGEgcmVnaW9uIGNvbHVtbiB1c2luZyB0aGUgY291bnR5IGRhdGEgYW5kIHRoZSBPTUIgTVNBIChNZXRyb3BvbGl0YW4gU3RhdGlzdGljYWwgQXJlYSkgZGVmaW5pdGlvbnMKCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKHJlZ2lvbiA9IAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIEx1aXMgT2Jpc3BvIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiS2VybiIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbiBCZXJuYXJkaW5vIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FudGEgQmFyYmFyYSIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlZlbnR1cmEiICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJMb3MgQW5nZWxlcyIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk9yYW5nZSIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlJpdmVyc2lkZSIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbiBEaWVnbyIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkltcGVyaWFsIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiS2luZyIgJiBTdGF0ZSA9PSAiV0EiLCAiU2VhdHRsZSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTbm9ob21pc2giICYgU3RhdGUgPT0gIldBIiwgIlNlYXR0bGUiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUGllcmNlIiAmIFN0YXRlID09ICJXQSIsICJTZWF0dGxlIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkNsYWNrYW1hcyIgJiBTdGF0ZSA9PSAiT1IiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQ29sdW1iaWEiICYgU3RhdGUgPT0gIk9SIiwgIlBvcnRsYW5kIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk11bHRub21haCIgJiBTdGF0ZSA9PSAiT1IiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiV2FzaGluZ3RvbiIgJiBTdGF0ZSA9PSAiT1IiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiWWFtaGlsbCIgJiBTdGF0ZSA9PSAiT1IiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQ2xhcmsiICYgU3RhdGUgPT0gIldBIiwgIlBvcnRsYW5kIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNrYW1hbmlhIiAmIFN0YXRlID09ICJXQSIsICJQb3J0bGFuZCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJEZW52ZXIiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJBcmFwYWhvZSIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkplZmZlcnNvbiIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkFkYW1zIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiRG91Z2xhcyIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkJyb29tZmllbGQiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsICAgIAogICAgaWZlbHNlKENvdW50eSA9PSAiRWxiZXJ0IiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUGFyayIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkNsZWFyIENyZWVrIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQWxhbWVkYSIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQ29udHJhIENvc3RhIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJNYXJpbiIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTW9udGVyZXkiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk5hcGEiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbiBCZW5pdG8iICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbiBGcmFuY2lzY28iICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbiBNYXRlbyIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FudGEgQ2xhcmEiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNhbnRhIENydXoiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNvbGFubyIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU29ub21hIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICAgICAgICAgTkEpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkKCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKHJlZ2lvbiA9IAogICAgaWZlbHNlKENvdW50eSA9PSAiS2luZ3MiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlF1ZWVucyIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTmV3IFlvcmsiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkJyb254IiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJSaWNobW9uZCIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiV2VzdGNoZXN0ZXIiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkJlcmdlbiIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiSHVkc29uIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJQYXNzYWljIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJQdXRuYW0iICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlJvY2tsYW5kIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTdWZmb2xrIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJOYXNzYXUiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk1pZGRsZXNleCIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTW9ubW91dGgiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk9jZWFuIiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTb21lcnNldCIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiRXNzZXgiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlVuaW9uIiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJNb3JyaXMiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlN1c3NleCIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiSHVudGVyZG9uIiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJQaWtlIiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICByZWdpb24pKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkKCgojIGNvZGUgbm9yIGNhbCByZWdpb24gYXMgYWxsIG90aGVycyBpbiBDQSBub3QgYWxyZWFkeSBkZWZpbmVkCgpkYXRhY2xlYW4gPC0gZGF0YWNsZWFuICU+JQogIG11dGF0ZShyZWdpb24gPSAKICAgIGlmZWxzZShTdGF0ZSA9PSAiQ0EiICYgaXMubmEocmVnaW9uKSA9PSBUUlVFLCAiTm9yIENhbCIsIHJlZ2lvbikpCgoKI1JlbW92aW5nIENvbHVtbnMgdGhhdCBwcm92aWRlIG5vIGJlbmVmaXQgCgpkYXRhY2xlYW4gPC0gc3Vic2V0KGRhdGFjbGVhbixzZWxlY3QgPSAtYyhBc3NpZ25tZW50Lk51bWJlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuaGFzLkhpc3RvcmljYWwuTW5ncgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFN1ZmZpeAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuRGF0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuTWFuYWdlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuUm9sZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuVGl0bGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50LlN0YXR1cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFN0cmF0ZWd5CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUHJvZ3Jlc3MuTGV2ZWwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50Lkdyb3VwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQXNzaWdubWVudC5DYXRlZ29yeQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEZ1bmRpbmcuTWV0aG9kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEV4cGVjdGVkLkJvb2suRGF0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxRdWFsaWZpY2F0aW9uLkFtb3VudAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxFeHBlY3RlZC5Cb29rLkFtb3VudAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxFeHBlY3RlZC5Cb29rLkRhdGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsSGFyZC5HaWZ0LlRvdGFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFNvZnQuQ3JlZGl0LlRvdGFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFRvdGFsLkFzc2lnbm1lbnQuR2lmdHMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsTm8ub2YuUGxlZGdlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxQcm9wb3NhbC4uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFByb3Bvc2FsLk5vdGVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEhILkxpZmUuU3BvdXNlLkNyZWRpdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxMYXN0LkNvbnRhY3QuQnkuTWFuYWdlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxYLi5vZi5Db250YWN0cy5CeS5NYW5hZ2VyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLERvbm9yU2VhcmNoLlJhbmdlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLGlXYXZlLlJhbmdlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFdlYWx0aEVuZ2luZS5SYW5nZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxQaGlsYW50aHJvcGljLkNvbW1pdG1lbnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkKI2NsZWFuaW5nIHVwIHppcCBjb2RlcyByZW1vdmluZyAtNCBhZnRlciAKZGF0YWNsZWFuJFppcCA8LSBnc3ViKGRhdGFjbGVhbiRaaXAsIHBhdHRlcm49Ii0uKiIsIHJlcGxhY2VtZW50ID0gIiIpCgojYWRkaW5nIHppcCBjb2RlIGRhdGEgYW5kIGNvbHVtbiAKemlwIDwtIHJlYWQuY3N2KGhlcmU6OmhlcmUoImZpbmFsX3Byb2plY3QiLCAiU2FsYXJ5X1ppcGNvZGUuY3N2IiksCiAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgIHN0cmlwLndoaXRlID0gVFJVRSwKICAgICAgICAgICAgICAgICBuYS5zdHJpbmdzID0gIiIpCgoKI2FkZGluZyB6aXAgc2FsYXJ5IGNvbHVtbgpkYXRhY2xlYW4gPC1kYXRhY2xlYW4gJT4lCiAgICBtdXRhdGUoemlwY29kZV9zbHJ5ID0gVkxPT0tVUChaaXAsIHppcCwgTkFNRSwgUzE5MDJfQzAzXzAwMkUpKQoKI3NscnkgcmFuZ2UgCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKHppcHNscnlfcmFuZ2UgPSAKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAxMDAwMDo4OTk5OSwgIjkwSy05OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDkwMDAwOjk5OTk5LCAiOTBLLTk5SyIsCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgMTAwMDAwOjE0OTk5OSwgIjEwMEstMTQ5SyIsIAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDE1MDAwMDoxOTk5OTksICIxNTBLLTE5OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDIwMDAwMDoyNDk5OTksICIyMDBLLTI0OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDI1MDAwMDoyOTk5OTksICIyNTBLLTI5OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDMwMDAwMDozNDk5OTksICIzMDBLLTM0OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDM1MDAwMDozOTk5OTksICIzNTBLLTM5OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDQwMDAwMDo0OTk5OTksICI0MDBLLTQ5OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDUwMDAwMDo5OTk5OTksICI1MDBLLTk5OUsiLAogICAgTkEpKSkpKSkpKSkpKQoKc3VtKGlzLm5hKGRhdGFjbGVhbiR6aXBjb2RlX3NscnkpKQoKI2FkZGluZyBzY2hvbGFyc2hpcCBkYXRhICh5L24pCnNjaGxyIDwtIHJlYWQuY3N2KGhlcmU6OmhlcmUoImZpbmFsX3Byb2plY3QiLCAic2Nob2xhcnNoaXAuY3N2IiksCiAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgIHN0cmlwLndoaXRlID0gVFJVRSwKICAgICAgICAgICAgICAgICBuYS5zdHJpbmdzID0gIiIpCgojYWRkaW5nIHNjaG9sYXJzaGlwIGNvbHVtbgpkYXRhY2xlYW4gPC1kYXRhY2xlYW4gJT4lCiAgICBtdXRhdGUoc2Nob2xhcnNoaXAgPSBWTE9PS1VQKElELCBzY2hsciwgSUQsIFNDSE9MQVJTSElQKSkgCgojcmVwbGFjaW5nIE5BIHdpdGggMCAKIGRhdGFjbGVhbiRzY2hvbGFyc2hpcCA8LSByZXBsYWNlX25hKGRhdGFjbGVhbiRzY2hvbGFyc2hpcCwnMCcpCiAKI3JlcGxhY2luZyBZIHdpdGggMSAKZGF0YWNsZWFuJHNjaG9sYXJzaGlwPC1pZmVsc2UoZGF0YWNsZWFuJHNjaG9sYXJzaGlwPT0iWSIsMSwwKQoKI2NoZWNraW5nIGhvdyBtYW55IGFyZSBOCnRhYmxlKGRhdGFjbGVhbiRzY2hvbGFyc2hpcCkKCgojY2hlY2tpbmcgYW5kIGRlbGV0aW5nIHNjaG9sYXJzaGlwIGNvbHVtbiAKY2xhc3MoZGF0YWNsZWFuJHNjaGxyX2ZjdCkKZGF0YWNsZWFuID0gc3Vic2V0KGRhdGFjbGVhbiwgc2VsZWN0ID0gLWMoc2Nob2xhcnNoaXApKQogIAojY2hlY2tpbmcgZm9yIGR1cGxpY2F0ZXMgTiA+MSBpbmRpY2F0ZXMgYSByZWNvcmRzIHZhbHVlcyBhcmUgaW4gdGhlIGZpbGUgdHdpY2UgCmRhdGFjbGVhbiAlPiUgZ3JvdXBfYnkoSUQpICU+JSBjb3VudCgpICU+JSBhcnJhbmdlKGRlc2MobikpCgojcmVtb3ZpbmcgZHVwbGljYXRlZCByZWNvcmRzCgpkYXRhY2xlYW4gPC0gdW5pcXVlKGRhdGFjbGVhbikKCiNuID0gMSBubyBJRCB3aXRoIG11bHRpcGxlIHJlY29yZHMgY2xlYW5lZCBvZiBkdXBlcwpkYXRhY2xlYW4gJT4lIGdyb3VwX2J5KElEKSAlPiUgY291bnQoKSAlPiUgYXJyYW5nZShkZXNjKG4pKQoKYGBgCgoKMWQgQ3JlYXRpbmcgbWFueSBtYW55IGZhY3RvciB2YXJpYWJsZXMKCmBgYHtyfQoKZGF0YWNsZWFuIDwtIAogIGRhdGFjbGVhbiAlPiUgCiAgI1NFWAogIG11dGF0ZShzZXhfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKFNleCksCnNleF9zaW1wbGUgPSAKICAgIGZjdF9sdW1wX24oU2V4LCBuID0gNCksCiNNQVJSSUVECm1hcnJpZWRfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKE1hcnJpZWQpLAogICNET05PUiBTRUdNRU5UCiAgZG9ub3JzZWdfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKERvbm9yLlNlZ21lbnQpLAogICAgICAgICBkb25vcnNlZ19zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKERvbm9yLlNlZ21lbnQsIG4gPSA0KSwKICAjQ09OVEFDVCBSVUxFCiAgICAgICAgIGNvbnRhY3RfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENvbnRhY3QuUnVsZXMpLAogICAgICAgICBjb250YWN0X3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ29udGFjdC5SdWxlcywgbiA9IDQpLAogICNTUE9VU0UgTUFJTAogICAgICAgICBzcG9tYWlsX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShTcG91c2UuTWFpbC5SdWxlcyksCiAgICAgICAgIHNwb21haWxfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihTcG91c2UuTWFpbC5SdWxlcywgbiA9IDQpLAogICNKT0IgVElUTEUKICAgICAgICAgam9idGl0bGVfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKEpvYi5UaXRsZSksCiAgICAgICAgIGpvYnRpdGxlX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oSm9iLlRpdGxlLCBuID0gNSksCiAgI0RFR1JFRSBUWVBFIDEKICAgICAgICAgZGVnMV9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoRGVncmVlLlR5cGUuMSksCiAgICAgICAgIGRlZzFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihEZWdyZWUuVHlwZS4xLCBuID0gNSksCiAgI0RFR1JFRSBUWVBFIDIKICAgICAgICAgZGVnMl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoRGVncmVlLlR5cGUuMiksCiAgICAgICAgIGRlZzJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihEZWdyZWUuVHlwZS4yLCBuID0gNSksCiAgI01BSk9SIDEKICAgICAgICAgbWFqMV9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWFqb3IuMSksCiAgICAgICAgIG1hajFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihNYWpvci4xLCBuID0gNSksCiAgI01BSk9SIDIKICAgICAgICAgbWFqMl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWFqb3IuMiksCiAgICAgICAgIG1hajJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihNYWpvci4yLCBuID0gNSksCiAgI01JTk9SIDEKICAgICAgICAgbWluMV9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWlub3IuMSksCiAgICAgICAgIG1pbjFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihNaW5vci4xLCBuID0gNSksCiAgI01JTk9SIDIKICAgICAgICAgbWluMl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoTWlub3IuMiksCiAgICAgICAgIG1pbjJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihNaW5vci4yLCBuID0gNSksCiAgI1NDSE9PTCAxCiAgICAgICAgIHNjaG9vbDFfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKFNjaG9vbC4xKSwKICAgICAgICAgc2Nob29sMV9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKFNjaG9vbC4xLCBuID0gNSksCiAgI1NDSE9PTCAyCiAgICAgICAgIHNjaG9vbDJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKFNjaG9vbC4yKSwKICAgICAgICAgc2Nob29sMl9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKFNjaG9vbC4yLCBuID0gNSksCiAgI0lOU1RJVFVUSU9OIFRZUEUKICAgICAgICAgaW5zdHR5cGVfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKEluc3RpdHV0aW9uLlR5cGUpLAogICAgICAgICBpbnN0dHlwZV9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKEluc3RpdHV0aW9uLlR5cGUsIG4gPSA1KSwKICAjRVhUUkFDVVJSSUNVTEFSCiAgICAgICAgIGV4dHJhX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShFeHRyYWN1cnJpY3VsYXIpLAogICAgICAgICBleHRyYV9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKEV4dHJhY3VycmljdWxhciwgbiA9IDUpLAogICNISCBGSVJTVCBHSUZUIEZVTkQKICAgICAgICAgaGhmaXJzdGdpZnRfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKEhILkZpcnN0LkdpZnQuRnVuZCksCiAgICAgICAgIGhoZmlyc3RnaWZ0X3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oSEguRmlyc3QuR2lmdC5GdW5kLCBuID0gNSksCiNDSElMRCAxIEVOUk9MTCBTVEFUVVMKICAgICAgICAgY2gxX2Vucm9sbF9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5FbnJvbGwuU3RhdHVzKSwKICAgICAgICAgY2gxX2Vucm9sbF9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjEuRW5yb2xsLlN0YXR1cywgbiA9IDQpLAojQ0hJTEQgMSBNQUpPUgogICAgICAgICBjaDFfbWFqX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4xLk1ham9yKSwKICAgICAgICAgY2gxX21hal9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjEuTWFqb3IsIG4gPSA0KSwKI0NISUxEIDEgTUlOT1IKICAgICAgICAgY2gxX21pbl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5NaW5vciksCiAgICAgICAgIGNoMV9taW5fc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLk1pbm9yLCBuID0gNCksCiNDSElMRCAxIFNDSE9PTAogICAgICAgICBjaDFfc2Nob29sX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4xLlNjaG9vbCksCiAgICAgICAgIGNoMV9zY2hvb2xfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLlNjaG9vbCwgbiA9IDQpLAojQ0hJTEQgMSBGRUVERVIKICAgICAgICAgY2gxX2ZlZWRlcl9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5GZWVkZXIuU2Nob29sKSwKICAgICAgICAgY2gxX2ZlZWRlcl9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjEuRmVlZGVyLlNjaG9vbCwgbiA9IDQpLAojQ0hJTEQgMiBFTlJPTEwgU1RBVFVTCiAgICAgICAgIGNoMV9lbnJvbGxfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuRW5yb2xsLlN0YXR1cyksCiAgICAgICAgIGNoMl9lbnJvbGxfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4yLkVucm9sbC5TdGF0dXMsIG4gPSA0KSwKI0NISUxEIDIgTUFKT1IKICAgICAgICAgY2gyX21hal9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMi5NYWpvciksCiAgICAgICAgIGNoMl9tYWpfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4yLk1ham9yLCBuID0gNCksCiNDSElMRCAyIE1JTk9SCiAgICAgICAgIGNoMl9taW5fZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuTWlub3IpLAogICAgICAgICBjaDJfbWluX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5NaW5vciwgbiA9IDQpLAojQ0hJTEQgMiBTQ0hPT0wKICAgICAgICAgY2gyX3NjaG9vbF9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMi5TY2hvb2wpLAogICAgICAgICBjaDJfc2Nob29sX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5TY2hvb2wsIG4gPSA0KSwKI0NISUxEIDIgRkVFREVSCiAgICAgICAgIGNoMl9mZWVkZXJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuRmVlZGVyLlNjaG9vbCksCiAgICAgICAgIGNoMl9mZWVkZXJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4yLkZlZWRlci5TY2hvb2wsIG4gPSA0KSwKICAgICkKCgoKI2NoZWNraW5nIHRvIHNlZSBpZiBpdHMgYSBmYWN0b3IKI2NsYXNzKGRhdGFjbGVhbiRzZXhfZmN0KQojY2xhc3MoZGF0YWNsZWFuJGRvbm9yc2VnX2ZjdCkKI2NsYXNzKGRhdGFjbGVhbiRjb250YWN0X2ZjdCkKI2NsYXNzKGRhdGFjbGVhbiRzcG9tYWlsX2ZjdCkKCiNjaGVja2luZyBsZXZlbHMKI2xldmVscyhkYXRhY2xlYW4kc2V4X3NpbXBsZSkKI2xldmVscyhkYXRhY2xlYW4kZG9ub3JzZWdfc2ltcGxlKQojbGV2ZWxzKGRhdGFjbGVhbiRjb250YWN0X3NpbXBsZSkKI2xldmVscyhkYXRhY2xlYW4kc3BvbWFpbF9zaW1wbGUpCiNsZXZlbHMoZGF0YWNsZWFuJGhoZmlyc3RnaWZ0X3NpbXBsZSkKCiNjcmVhdGluZyBhIHRhYmxlIGFnYWluc3QgU2V4IGNvbHVtbiAKI3RhYmxlKGRhdGFjbGVhbiRzZXhfZmN0LCBkYXRhY2xlYW4kc2V4X3NpbXBsZSkKCgpgYGAKClJlZ2lvbiBBbmFseXNpcwoKYGBge3J9CiNncm91cGluZyBieSByZWdpb24gYW5kIGFuYWx5emluZyAKZGF0YWNsZWFuICU+JQogIGdyb3VwX2J5KHJlZ2lvbikgJT4lCiAgc3VtbWFyaXNlKENvdW50ID0gbGVuZ3RoKHJlZ2lvbiksCiAgICAgICAgICAgIG1lYW5fdG90YWxfZ2l2ID0gbWVhbihISC5MaWZldGltZS5HaXZpbmcpKSAlPiUKICBhcnJhbmdlKC1Db3VudCkgJT4lCiAgZmlsdGVyKENvdW50ID49IDEwMCkgJT4lCiAgbXV0YXRlKG1lYW5fdG90YWxfZ2l2ID0gZG9sbGFyKG1lYW5fdG90YWxfZ2l2KSkgJT4lCiAga2FibGUoY29sLm5hbWVzID0gYygiUmVnaW9uIiwgIkNvdW50IiwgIk1lYW4gSEggTGlmZXRpbWUgR2l2aW5nIiksIGFsaWduPXJlcCgnYycsIDMpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEYpCiAgCgpgYGAKCgpEb25vclNlZ21lbnQgQW5hbHlzaXMKCmBgYHtyfQojZ3JvdXBpbmcgYnkgZG9ub3JzZWdtZW50IGFuZCBhbmFseXppbmcgCmRhdGFjbGVhbiAlPiUKICBncm91cF9ieShEb25vci5TZWdtZW50KSAlPiUKICBzdW1tYXJpc2UoQ291bnQgPSBsZW5ndGgoRG9ub3IuU2VnbWVudCksCiAgICAgICAgICAgIG1lYW5fdG90YWxfZ2l2ID0gbWVhbihISC5MaWZldGltZS5HaXZpbmcpKSAlPiUKICBhcnJhbmdlKC1Db3VudCkgJT4lCiAgZmlsdGVyKENvdW50ID49IDEwMCkgJT4lCiAgI2FkZGVkIHNjYWxlcyBwYWNrYWdlIHRvIGhhdmUgdGhlIHZhbHVlcyBzaG93IGluIGRvbGxhciAKICBtdXRhdGUobWVhbl90b3RhbF9naXYgPSAoZG9sbGFyKG1lYW5fdG90YWxfZ2l2KSkpICU+JQogIGthYmxlKGNvbC5uYW1lcyA9IGMoIkRvbm9yIFNlZ21lbnQiLCAiQ291bnQiLCAiTWVhbiBISCBMaWZldGltZSBHaXZpbmciKSwgYWxpZ249cmVwKCdjJywgMykpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikKICAKCmBgYAoKRmlyc3QgZ2lmdCBzaXplIApgYGB7cn0KYXEgPC0gcXVhbnRpbGUoZGF0YWNsZWFuJEhILkZpcnN0LkdpZnQuQW1vdW50LCBwcm9icyA9IGMoLjI1LC41MCwuNzUsLjksLjk5KSwgbmEucm0gPSBUUlVFKQoKYXEgPC0gYXMuZGF0YS5mcmFtZShhcSkKCmFxJGFxIDwtIGRvbGxhcihhcSRhcSkKCmFxICU+JQogIGthYmxlKGNvbC5uYW1lcyA9ICJRdWFudGlsZSIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRikKICAKCmBgYApDb25zZWN1dGl2ZSBnaXZpbmcKYGBge3J9CiNjb25zZWN1dGl2ZSB5ZWFycyBvZiBnaXZpbmcgCmRhdGFjbGVhbiAlPiUKICBmaWx0ZXIoTWF4LkNvbnNlYy5GaXNjYWwuWWVhcnMgPiAwKSAlPiUKICBnZ3Bsb3QoYWVzKE1heC5Db25zZWMuRmlzY2FsLlllYXJzKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiMwMDI4NDUiLCBiaW5zID0gMjApICsgCiAgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKwogIGdndGl0bGUoIkNvbnNlY3V0aXZlIFllYXJzIG9mIEdpdmluZyBEaXN0cmlidXRpb24iKSArIAogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEyMCwyKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMDAwMDAwMCw1MDAwKSkgCgoKCmBgYAoKTGlmZXRpbWUgZ2l2aW5nIGJhc2VkIG9uIG51bWJlciBvZiBjaGlsZHJlbiAKCmBgYHtyfQpkYXRhY2xlYW4gJT4lCiAgZmlsdGVyKEhILkxpZmV0aW1lLkdpdmluZyA8PSAxMDAwMCkgJT4lCiAgZmlsdGVyKEhILkxpZmV0aW1lLkdpdmluZyA+IDApICU+JQogIG11dGF0ZShgTm9fb2ZfQ2hpbGRyZW5gID0gYXMuZmFjdG9yKGBOb19vZl9DaGlsZHJlbmApKSAlPiUKICBnZ3Bsb3QoYWVzKEhILkxpZmV0aW1lLkdpdmluZywgZmlsbCA9IGBOb19vZl9DaGlsZHJlbmApKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCkgKyB0aGVtZV9lY29ub21pc3Rfd2hpdGUoKSArCiAgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTAwMDAwLDEwMDApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEwMDAwMDAwMCw1MDAwKSkgKwogIGdndGl0bGUoIkdpdmluZyBkaXN0cmlidXRpb24gYW5kIG51bWJlciBvZiBjaGlsZHJlbiIpKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiIzAwMjg0NSIsICIjMDBjZmNjIiwgIiNmZjk5NzMiKSkKCgoKYGBgCgoKTWVhbiwgTWVkaWFuLCBhbmQgQ291bnQgb2YgR2l2aW5nIGluIEFnZSBSYW5nZXMgCgpgYGB7cn0KCmFnZV9yYW5nZV9naXZpbmcgPC0gZGF0YWNsZWFuICU+JQogIGdyb3VwX2J5KGFnZV9yYW5nZSkgJT4lCiAgc3VtbWFyaXNlKGF2Z19naXZpbmcgPSBtZWFuKEhILkxpZmV0aW1lLkdpdmluZywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbWVkX2dpdmluZyA9IG1lZGlhbihISC5MaWZldGltZS5HaXZpbmcsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIGFtb3VudF9vZl9wZW9wbGVfaW5fYWdlX3JhbmdlID0gbigpKQoKCmdsaW1wc2UoYWdlX3JhbmdlX2dpdmluZykKCmBgYAoKCgoKCiMjIFBhcnQgMgoKMmEpIFBsb3R0aW5nIGF2ZXJhZ2UgZ2l2aW5nIGJ5IGFnZSByYW5nZSAKCgpgYGB7cn0KCmFnZV9yYW5nZV9naXZpbmcgPC0KICBhZ2VfcmFuZ2VfZ2l2aW5nICU+JQogIG11dGF0ZShhZ2VfcmFuZ2UgPSBmYWN0b3IoYWdlX3JhbmdlKSkKCmdncGxvdChhZ2VfcmFuZ2VfZ2l2aW5nLCBhZXMoYWdlX3JhbmdlLCBhdmdfZ2l2aW5nKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0PTEpKQoKCmBgYAoKCjJiKSBDb3VudCBvZiBkb25vcnMgYmFzZWQgb24gYWdlIHJhbmdlIChhbm90aGVyIHdheSB0byBsb29rIGF0IGl0KQoKCmBgYHtyfQoKZ2dwbG90KGRhdGFjbGVhbiwgCiAgICAgICBhZXMoYWdlX3JhbmdlKSkgKyAKICAgICAgIGdlb21fYmFyKCkgKyAKICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3Q9MSkpICsgCiAgbGFicyh0aXRsZSA9ICJDb3VudCBvZiBBZ2UgUmFuZ2VzIiwgeCA9ICIiLCB5ID0gIiIpCiAgCgpgYGAKCjJjKSBCb3hwbG90IG9mIHRoZSBBZ2UgUmFuZ2VzIEFnYWluc3QgdGhlIExpZmV0aW1lIEdpdmluZyBBbW91bnRzIHdpdGggYSBsb2cgc2NhbGUgYXBwbGllZCAtIHRoZSByZWFzb24gd2UgYXBwbGllZCBsb2cgc2NhbGUgaXMgdG8gcmVzb2x2ZSBpc3N1ZXMgd2l0aCB2aXN1YWxpemF0aW9ucyB0aGF0IHNrZXcgdG93YXJkcyBsYXJnZSB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuIAoKCmBgYHtyfQoKZ2dwbG90KGRhdGFjbGVhbiwgYWVzKGFnZV9yYW5nZSxISC5MaWZldGltZS5HaXZpbmcsZmlsbCA9IGFnZV9yYW5nZSkpICsgCiAgZ2VvbV9ib3hwbG90KAogIG91dGxpZXIuY29sb3VyID0gInJlZCIpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsaGp1c3Q9MSkpCiAgCgpgYGAKCjJkKSBTcGxpdHRpbmcgYnkgYWdlIGFuZCBnZW5kZXIgCgoKYGBge3J9CgoKI2NyZWF0aW5nIGJveHBsb3RzIApkYXRhY2xlYW4gJT4lIAogIGZpbHRlcihBZ2UgPCAxMDApICU+JSAjcmVtb3ZpbmcgdGhlIHdlaXJkIG91dGxpZXJzIHRoYXQgYXJlIG92ZXIgMTAwIAogIGZpbHRlcihTZXggJWluJSBjKCJNIiwgIkYiKSkgJT4lCiAgZ2dwbG90KGFlcyhTZXgsIEFnZSkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICB0aGVtZV9lY29ub21pc3QoKSArIAogIGdndGl0bGUoIkFnZXMgb2YgRG9ub3JzIEJhc2VkIG9uIEdlbmRlciIpICsgCiAgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkKICAKCmBgYApHaXZpbmcgYnkgZ2VuZGVyCgoKYGBge3J9CgojcmVtb3ZlIE5BcyBVIFgKCmRhdGFjbGVhbjIgPC0gZGF0YWNsZWFuICU+JQogIGZpbHRlcihTZXggJWluJSBjKCJNIiwgIkYiKSkgCgpxIDwtIGdncGxvdChkYXRhY2xlYW4yKSAKcSArIHN0YXRfc3VtbWFyeV9iaW4oCiAgYWVzKHkgPSBISC5MaWZldGltZS5HaXZpbmcsIHggPSBTZXgpLCAKICBmdW4ueSA9ICJtZWFuIiwgZ2VvbSA9ICJiYXIiKSAKICAKc3VtbWFyeShkYXRhY2xlYW4kc2V4X3NpbXBsZSkKCmBgYAoKTWVhbiBhZ2UgYnkgZ2VuZGVyCgoKYGBge3J9CiNicmVha2Rvd24gb2Ygc2V4cyAKdGFsbHkoZ3JvdXBfYnkoZGF0YWNsZWFuLCBTZXgpKQoKc3VtbWFyaXplKGdyb3VwX2J5KGRhdGFjbGVhbiwgU2V4KSwgCiAgICAgICAgICBhdmdfZ2l2aW5nID0gbWVhbihISC5MaWZldGltZS5HaXZpbmcsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICBhdmdfYWdlID0gbWVhbihBZ2UsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICBtZWRfYWdlID0gbWVkaWFuKEFnZSwgbmEucm0gPSBUUlVFKSkKCiNncm91cGluZyBieSBzZXggYW5kIGFnZSByYW5nZSBmb3Igc2xpZGVzIAp0YWxseShncm91cF9ieShkYXRhY2xlYW4sIFNleCwgYWdlX3JhbmdlKSkKCgoKYGBgCgoKCjJlKSBEaXN0cmlidXRpb24gb2YgcGVvcGxlIGluIHRoZSBzdGF0ZXMgdGhhdCB0aGV5IGxpdmUuCgpgYGB7cn0KCiAgZGF0YWNsZWFuICU+JQogIG11dGF0ZShTdGF0ZSA9IGlmZWxzZShTdGF0ZSA9PSAiICIsICJOQSIsIFN0YXRlKSkgJT4lCiAgZmlsdGVyKFN0YXRlICE9ICJOQSIpICU+JQogIGdyb3VwX2J5KFN0YXRlKSAlPiUKICBzdW1tYXJpc2UoQ291bnQgPSBsZW5ndGgoU3RhdGUpKSAlPiUKICBmaWx0ZXIoQ291bnQgPiA4MDApICU+JQogIGFycmFuZ2UoLUNvdW50KSAlPiUKICBrYWJsZShjb2wubmFtZXMgPSBjKCJEb25vcidzIFN0YXRlIiwgIkNvdW50IikpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJjb25kZW5zZWQiKSwKICAgICAgICAgICAgICAgIGZ1bGxfd2lkdGggPSBGKQogIAogCiAgCiAgCgoKYGBgCgoyZikgTG9va2luZyBhdCBhbGwgZG9ub3JzIGZpcnN0IGdpZnQgYW1vdW50LiA3NSUgbWFkZSBhIGZpcnN0IGdpZnQgb2YgPDEwMC4gCgpgYGB7cn0KCiBub19ub25fZG9ub3JzIDwtIGRhdGFjbGVhbiAlPiUKICBmaWx0ZXIoTGlmZXRpbWUuR2l2aW5nICE9IDApCiAgCm5kIDwtIHF1YW50aWxlKG5vX25vbl9kb25vcnMkSEguRmlyc3QuR2lmdC5BbW91bnQsIHByb2JzID0gYyguMjUsLjUwLC43NSwuOSwuOTkpLCBuYS5ybSA9IFRSVUUpCgpuZCA8LSBhcy5kYXRhLmZyYW1lKG5kKQoKbmQgJT4lCiAga2FibGUoY29sLm5hbWVzID0gIlF1YW50aWxlIikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwKICAgICAgICAgICAgICAgIGZ1bGxfd2lkdGggPSBGKQogIAogIAoKCmBgYAoKCgojIyBNb2RlbGluZyBmb3IgeW91IAoKU3BsaXQgZGF0YQoKYGBgIHtyfQoKCgoKI2NvbnZlcnRpbmcgbWFycmllZCBZIGFuZCBOIHRvIDEgYW5kIDAgCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgICAgIG11dGF0ZShNYXJyaWVkX3NpbXBsZSA9IGlmZWxzZShNYXJyaWVkID09ICJOIiwwLDEpKQoKCmRhdGFjbGVhbiA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKGhoLmxpZmV0aW1lLmdpdmluZ19mY3QgPSBhcy5mYWN0b3IoSEguTGlmZXRpbWUuR2l2aW5nKSkgJT4lCiAgbXV0YXRlKEhILkxpZmV0aW1lLkdpdmluZy5QbHVzID0gbG9nKEhILkxpZmV0aW1lLkdpdmluZyArIDEpKQoKCmxpYnJhcnkoInJzYW1wbGUiKQoKZGF0YV9zcGxpdCA8LSBpbml0aWFsX3NwbGl0KGRhdGFjbGVhbiwgcHJvcCA9IDAuNzUpCgpkYXRhX3RyYWluIDwtIHRyYWluaW5nKGRhdGFfc3BsaXQpCmRhdGFfdGVzdCA8LSB0ZXN0aW5nKGRhdGFfc3BsaXQpCgpgYGAKCgoKYGBge3J9CnAgPC0gZGF0YWNsZWFuICU+JQogIGdncGxvdChhZXMoQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwLCBmaWxsID0gImJsdWUiKSArIHRoZW1lX2Vjb25vbWlzdF93aGl0ZSgpICsKICBnZ3RpdGxlKCJPdmVyYWxsIERvbm9yIEFnZSBEaXN0cmlidXRpb24iKSArIAogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSg1LDEwMCxieSA9IDIwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMjAsMTAwLGJ5ID0gMjApKSArIHhsaW0oYygyMCwxMDApKQoKZ2dwbG90bHkocCkKICAKcAoKZ2dwbG90KGRhdGEgPSBkYXRhY2xlYW4sIGFlcyh4ID0gQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsID0iYmx1ZSIpKyB4bGltKGMoMjAsMTAwKSkKCiAgCgoKYGBgCgpBbm90aGVyIEhpc3RvZ3JhbQoKCmBgYHtyfQoKZGF0YWNsZWFuICU+JQogIGZpbHRlcihBZ2UgPj0gMTApICU+JQogIGZpbHRlcihBZ2UgPD0gOTApICU+JQogIGdncGxvdChhZXMoQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiMwMDI4NDUiLCBiaW5zID0gMjApICsgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKwogIGdndGl0bGUoIk92ZXJhbGwgRG9ub3IgQWdlIERpc3RyaWJ1dGlvbiIpICsgCiAgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTIwLDUpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEwMDAwMDAwLDIwMDApKSAKCmBgYApBZ2UgZGlzdHJpYnV0aW9uIGJ5IGdlbmRlciAKCmBgYHtyfQojQWdlIEdlbmRlciBmaWx0ZXJlZCBvdXQgYmVsb3cgMTUgYW5kIGFib3ZlIDkwIC0gYWxzbyByZW1vdmVkIFUgWCB0aGUgd2VpcmQgdmFsdWVzIApkYXRhY2xlYW4gJT4lCiAgZmlsdGVyKEFnZSA+PSAxNSkgJT4lCiAgZmlsdGVyKEFnZSA8PSA5MCkgJT4lCiAgbXV0YXRlKFNleCA9IGFzLmZhY3RvcihTZXgpKSAlPiUKICBmaWx0ZXIoU2V4ICE9ICJVIikgJT4lCiAgZmlsdGVyKFNleCAhPSAiWCIpICU+JQogIGdncGxvdChhZXMoQWdlLCBmaWxsID0gU2V4KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUpICsgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKwogIGdndGl0bGUoIkFnZSBEaXN0cmlidXRpb24gYnkgR2VuZGVyIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsMTApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDUwMDAwLDIwMDApKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjZmY5OTczIiwgIiMwMGNmY2MiKSkKYGBgCgpEb25vciBhZ2UgZGlzdHJpYnV0aW9uIGJ5IG1hcml0YWwgc3RhdHVzIAoKYGBge3J9CiNBZ2UgTWFyaXRhbCBTdGF0dXMKZGF0YWNsZWFuICU+JQogIGZpbHRlcihBZ2UgPj0gMjApICU+JQogIGZpbHRlcihBZ2UgPD0gODUpICU+JQogIGdncGxvdChhZXMoQWdlLCBmaWxsID0gTWFycmllZCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDI1KSArIHRoZW1lX2Vjb25vbWlzdF93aGl0ZSgpICsKICBnZ3RpdGxlKCJPdmVyYWxsIERvbm9yIEFnZSBEaXN0cmlidXRpb24gYnkgTWFyaXRhbCBTdGF0dXMiKSArIAogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEyMCw1KSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCw1MDAwMCwyMDAwKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI2ZmOTk3MyIsICIjMDBjZmNjIikpCmBgYAoKTGluZWFyIE1vZGVsIAoKYGBge3J9CiNUaGVzZSB3aWxsIGZvY3VzIG9uIHByZWRpY3Rpbmcgd2hldGhlciBhIGNvbnN0aXR1ZW50IGlzIGEgZG9ub3Igb3Igbm9uLWRvbm9yLiAKCgptb2QxbG0gPC0gbG0oIExpZmV0aW1lLkdpdmluZyB+IE1hcnJpZWRfc2ltcGxlLAogICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluKQoKbW9kMmxtIDwtIGxtKCBUb3RhbC5HaXZpbmcuWWVhcnMgfiBMaWZldGltZS5HaXZpbmcsCiAgICAgICAgICAgZGF0YSA9IGRhdGFfdHJhaW4pCgptb2QzbG0gPC0gbG0oIExpZmV0aW1lLkdpdmluZyB+IHJlZ2lvbiwKICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbikKCnN1bW1hcnkobW9kMWxtKQpzdW1tYXJ5KG1vZDJsbSkKc3VtbWFyeShtb2QzbG0pCiNpbmNyZWFzaW5nIHRoZSBnaXZpbmcgeWVhciBvbmUgeWVhciBpbmNyZWFzZSB0b3RhbCBnaXZpbmcgYnkgMC4wMDM1CgoKZ2dwbG90KGRhdGEgPSBkYXRhX3RyYWluLCBhZXMoeCA9IEFnZSwgeSA9IGxvZyhISC5MaWZldGltZS5HaXZpbmcpKSkgKyBnZW9tX3BvaW50KGFscGhhID0gMS8xMCkgKyBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkgKyBmYWNldF93cmFwKH5yZWdpb24pICsgdGhlbWVfY2xlYW4oYmFzZV9zaXplID0gOCkgKyBsYWJzKHggPSAiWCIsIHkgPSAiWSIpICsKICAgICAgZ2d0aXRsZSgiUmVnaW9uIikKCgpnZ3Bsb3QoZGF0YSA9IGRhdGFfdHJhaW4sIGFlcyh4ID0gQWdlLCB5ID0gbG9nKEhILkxpZmV0aW1lLkdpdmluZykpKSArIGdlb21fcG9pbnQoYWxwaGEgPSAxLzEwKSArIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKSArIGZhY2V0X3dyYXAofm5tYl9kZWdyZWUpICsgdGhlbWVfY2xlYW4oYmFzZV9zaXplID0gOCkgKyBsYWJzKHggPSAiWCIsIHkgPSAiWSIpICsKICAgICAgZ2d0aXRsZSgiTnVtYmVyIG9mIERlZ3JlZXMiKQoKCmdncGxvdChkYXRhID0gZGF0YV90cmFpbiwgYWVzKHggPSBBZ2UsIHkgPSBsb2coSEguRmlyc3QuR2lmdC5BbW91bnQpKSkgKyBnZW9tX3BvaW50KGFscGhhID0gMS8xMCkgKyBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkgKyBmYWNldF93cmFwKH5kb25vcnNlZ19mY3QpICsgdGhlbWVfY2xlYW4oYmFzZV9zaXplID0gOCkgKyBsYWJzKHggPSAiWCIsIHkgPSAiWSIpICsKICAgICAgZ2d0aXRsZSgiRG9ub3IgU2VnbWVudCIpCgojVGhpcyBwbG90IGFjdHVhbGx5IGhhcyBzb21lIGludGVyZXN0aW5nIHJlc3VsdHMKZ2dwbG90KGRhdGEgPSBkYXRhX3RyYWluLCBhZXMoeCA9IEFnZSwgeSA9IGxvZyhMaWZldGltZS5HaXZpbmcpKSkgKyBnZW9tX3BvaW50KGFscGhhID0gMS8xMCkgKyBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkgKyBmYWNldF93cmFwKH5Ob19vZl9DaGlsZHJlbikgKyB0aGVtZV9jbGVhbihiYXNlX3NpemUgPSA4KSArIGxhYnMoeCA9ICJYIiwgeSA9ICJZIikgKwogICAgICBnZ3RpdGxlKCIjIENoaWxkcmVuIikKCgpkYXRhX3RyYWluICU+JSAKICBzZWxlY3RfaWYoaXMuZmFjdG9yKSAlPiUgCiAgZ2xpbXBzZSgpCgoKYGBgCgoKCk1PUkUgTU9ERUxTCgpCaWcgbG9naXN0aWMgbW9kZWwKCmBgYHtyfQoKIyBTZXQgZmFtaWx5IHRvIGJpbm9taWFsIHRvIHNldCBsb2dpc3RpYyBmdW5jdGlvbgojIFJ1biB0aGUgbW9kZWwgb24gdGhlIHRyYWluaW5nIHNldAoKZG9ub3JfbG9naXQxIDwtCiAgZ2xtKGhoLmxpZmV0aW1lLmdpdmluZ19mY3QgfiBNYXJyaWVkX3NpbXBsZSwKICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwKICAgICAgZGF0YSA9IGRhdGFfdHJhaW4pCgpzdW1tYXJ5KGRvbm9yX2xvZ2l0MSkKCgpkb25vcl9sb2dpdDIgPC0KICBnbG0oaGgubGlmZXRpbWUuZ2l2aW5nX2ZjdCB+IE5vX29mX0NoaWxkcmVuLAogICAgICBmYW1pbHkgPSAiYmlub21pYWwiLAogICAgICBkYXRhID0gZGF0YV90cmFpbikKCnN1bW1hcnkoZG9ub3JfbG9naXQyKQoKCgoKCgoKI3N1bW1hcnkoZGF0YV90cmFpbiRtYWpvcl9naWZ0ZXIpCiNBc3NpZ25tZW50X2ZsYWcgdGFrZW4gb3V0IC0gbWF5IGFkZCBiYWNrCgpkb25vcl9sb2dpdDMgPC0KICBnbG0obWFqb3JfZ2lmdGVyIH4gTWFycmllZCArIE5vX29mX0NoaWxkcmVuICsgZG9ub3JzZWdfc2ltcGxlICsgIFRvdGFsLkdpdmluZy5ZZWFycyArIG5tYl9kZWdyZWUsCiAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsCiAgICAgIGRhdGEgPSBkYXRhX3RyYWluKQoKc3VtbWFyeShkb25vcl9sb2dpdDMpCmV4cChkb25vcl9sb2dpdDMkY29lZmZpY2llbnRzKQoKI3RyYWluaW5nIHByZWRpY3Rpb25zIGZvciBpbiBzYW1wbGUgcHJlZHMgCnByZWRzX3RyYWluIDwtIHByZWRpY3QoZG9ub3JfbG9naXQzLCBuZXdkYXRhID0gZGF0YV90cmFpbiwgdHlwZSA9ICJyZXNwb25zZSIpIAoKI3Rlc3QgcHJlZGljdHMgZm9yIE9PUyAob3V0IG9mIHNhbXBsZSkKcHJlZHNfdGVzdCA8LSBwcmVkaWN0KGRvbm9yX2xvZ2l0MywgbmV3ZGF0YSA9IGRhdGFfdGVzdCwgdHlwZSA9ICJyZXNwb25zZSIpCgpoZWFkKHByZWRzX3RyYWluKQpoZWFkKHByZWRzX3Rlc3QpCgoKCnJlc3VsdHNfdHJhaW4gPC0gZGF0YS5mcmFtZSgKICBgdHJ1dGhgID0gZGF0YV90cmFpbiAgICU+JSBzZWxlY3QobWFqb3JfZ2lmdGVyKSAlPiUgCiAgICBtdXRhdGUobWFqb3JfZ2lmdGVyID0gYXMubnVtZXJpYyhtYWpvcl9naWZ0ZXIpKSwKICBgQ2xhc3MxYCA9ICBwcmVkc190cmFpbiwKICBgdHlwZWAgPSByZXAoInRyYWluIixsZW5ndGgocHJlZHNfdHJhaW4pKQopCgpyZXN1bHRzX3Rlc3QgPC0gZGF0YS5mcmFtZSgKICBgdHJ1dGhgID0gZGF0YV90ZXN0ICAgJT4lIHNlbGVjdChtYWpvcl9naWZ0ZXIpICU+JSAKICAgIG11dGF0ZShtYWpvcl9naWZ0ZXIgPSBhcy5udW1lcmljKG1ham9yX2dpZnRlcikpLAogIGBDbGFzczFgID0gIHByZWRzX3Rlc3QsCiAgYHR5cGVgID0gcmVwKCJ0ZXN0IixsZW5ndGgocHJlZHNfdGVzdCkpCikKCnJlc3VsdHMgPC0gYmluZF9yb3dzKHJlc3VsdHNfdHJhaW4scmVzdWx0c190ZXN0KQoKZGltKHJlc3VsdHNfdHJhaW4pCmRpbShyZXN1bHRzX3Rlc3QpCmRpbShyZXN1bHRzKQoKbGlicmFyeSgncGxvdFJPQycpCgpwX3Bsb3QgPC0KICBnZ3Bsb3QocmVzdWx0cywKICAgICAgICAgYWVzKG0gPSBDbGFzczEsIGQgPSBtYWpvcl9naWZ0ZXIsIGNvbG9yID0gdHlwZSkpICsKICBnZW9tX3JvYyhsYWJlbHNpemUgPSAyLjUsCiAgICAgICAgICAgI1Rvb2sgdGhlIGxhYmVsc2l6ZSBkb3duIHRvIGF2b2lkIGN1dG9mZgogICAgICAgICAgIGN1dG9mZnMuYXQgPSBjKDAuNywwLjUsMC4zLDAuMSwwKSkgKwogI1dlIHJlbW92ZWQgc29tZSBvZiB0aGUgY3V0b2ZmcyB0byBhdm9pZCB0aGUgbWFzaHVwIG5lYXIgdGhlIG9yaWdpbi4KCiAgI0NoYW5nZWQgdGhlIHRoZW1lIHRvIGF2b2lkIGN1dG9mZiBwbG90IHZhbHVlcy4KICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE0KSArIAogIGxhYnMoeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwgCiAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIpICsKICAgICAgZ2d0aXRsZSgiUk9DIFBsb3Q6IFRyYWluaW5nIGFuZCBUZXN0IikKcHJpbnQocF9wbG90KSAKCgpwX3RyYWluIDwtCiAgZ2dwbG90KHJlc3VsdHNfdHJhaW4sCiAgICAgICAgIGFlcyhtID0gQ2xhc3MxLCBkID0gbWFqb3JfZ2lmdGVyLCBjb2xvciA9IHR5cGUpKSArCiAgZ2VvbV9yb2MobGFiZWxzaXplID0gMy41LAogICAgICAgICAgIGN1dG9mZnMuYXQgPSBjKDAuNywwLjUsMC4zLDAuMSwwKSkgKwogCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNikgKyAKICBsYWJzKHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsIAogICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiKSArCiAgICAgIGdndGl0bGUoIlJPQyBQbG90OiBUcmFpbmluZyBhbmQgVGVzdCIpCgpwX3Rlc3QgPC0KICBnZ3Bsb3QocmVzdWx0c190ZXN0LAogICAgICAgICBhZXMobSA9IENsYXNzMSwgZCA9IG1ham9yX2dpZnRlciwgY29sb3IgPSB0eXBlKSkgKwogIGdlb21fcm9jKGxhYmVsc2l6ZSA9IDMuNSwKICAgICAgICAgICBjdXRvZmZzLmF0ID0gYygwLjcsMC41LDAuMywwLjEsMCkpICsKIAogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTYpICsgCiAgbGFicyh4ID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUiLCAKICAgICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIikgKwogICAgICBnZ3RpdGxlKCJST0MgUGxvdDogVHJhaW5pbmcgYW5kIFRlc3QiKQoKI0NhbGN1bGF0aW5nIEFVQyBvZiBib3RoCnByaW50KGNhbGNfYXVjKHBfdHJhaW4pJEFVQykKcHJpbnQoY2FsY19hdWMocF90ZXN0KSRBVUMpCgoKCnN1bW1hcnkoZG9ub3JfbG9naXQzKQpjb2VmKGRvbm9yX2xvZ2l0MykKCgpgYGAKClJJREdFCgpgYGB7cn0KCmxpYnJhcnkoJ2dsbW5ldCcpCmxpYnJhcnkoJ2dsbW5ldFV0aWxzJykKCnJpZGdlX2ZpdDEgPC0gY3YuZ2xtbmV0KEhILkxpZmV0aW1lLkdpdmluZy5QbHVzIH4gc2V4X2ZjdCArIGRvbm9yc2VnX2ZjdCArIE5vX29mX0NoaWxkcmVuLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMCkKCiNBbHBoYSAwIHNldHMgdGhlIFJpZGdlCnByaW50KHJpZGdlX2ZpdDEpCnByaW50KHJpZGdlX2ZpdDEkbGFtYmRhLm1pbikKCnByaW50KHJpZGdlX2ZpdDEkbGFtYmRhLjFzZSkKcGxvdChyaWRnZV9maXQxKQoKYGBgCgpMQVNTTwoKYGBge3J9CgojVXNpbmcgY3YuZ2xtbmV0IGZyb20gY2xhc3MKI2xzKGRhdGFfdHJhaW4pIAojaXMuZmFjdG9yKGRhdGFfdHJhaW4kbWFqb3JfZ2lmdGVyKQojZ2xpbXBzZShkYXRhX3RyYWluJExpZmV0aW1lLkdpdmluZykKCiNkYXRhX3RyYWluICU+JSAKIyAgc2VsZWN0X2lmKGlzLmZhY3RvcikgJT4lIAojICBnbGltcHNlKCkKCgoKbGlicmFyeShnbG1uZXQpCmxpYnJhcnkoZ2xtbmV0VXRpbHMpCmxhc3NvX2ZpdCA8LSBjdi5nbG1uZXQoSEguTGlmZXRpbWUuR2l2aW5nLlBsdXMgfiBzZXhfZmN0ICsgam9idGl0bGVfc2ltcGxlICsgbm1iX2RlZ3JlZSArIHNjaG9vbDFfc2ltcGxlICsgaGhmaXJzdGdpZnRfc2ltcGxlICsgbWFqMV9zaW1wbGUgKyBkb25vcnNlZ19zaW1wbGUgKyBOb19vZl9DaGlsZHJlbiArIE1hcnJpZWQsCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFfdHJhaW4sCiAgICAgICAgICAgICAgICAgICAgICAgI0FscGhhIDEgZm9yIGxhc3NvCiAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxKQoKCnByaW50KGxhc3NvX2ZpdCRsYW1iZGEubWluKQojCnByaW50KGxhc3NvX2ZpdCRsYW1iZGEuMXNlKQoKcGxvdChsYXNzb19maXQpCgpgYGAKCgoKCmBgYHtyfQoKY29lZihsYXNzb19maXQpCiNEZWZhdWx0IHNldHRpbmcgaXMgbGFtYmRhLjFzZQoKI0Zyb20gdGhlIGJvb2sgLSBzaG93aW5nIGNvbnZlcmdlbmNlIHdpdGggbGFtYmRhIHZhbHVlcwpwbG90KGxhc3NvX2ZpdCRnbG1uZXQuZml0LCB4dmFyPSJsYW1iZGEiKQojYWJsaW5lKHY9bG9nKGMobGFzc29fZml0JGxhbWJkYS5taW4sIGxhc3NvX2ZpdCRsYW1iZGEuMXNlKSksIGx0eT0yKQoKYGBgCgoKCgoKYGBge3J9CgplbmV0X21vZCA8LSBjdmEuZ2xtbmV0KEhILkxpZmV0aW1lLkdpdmluZy5QbHVzIH4gc2V4X2ZjdCArIGpvYnRpdGxlX3NpbXBsZSArIG5tYl9kZWdyZWUgKyBzY2hvb2wxX3NpbXBsZSArIGhoZmlyc3RnaWZ0X3NpbXBsZSArIG1hajFfc2ltcGxlICsgZG9ub3JzZWdfc2ltcGxlICsgTm9fb2ZfQ2hpbGRyZW4gKyBNYXJyaWVkLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gc2VxKDAsMSwgYnkgPSAwLjEpKQoKcHJpbnQoZW5ldF9tb2QpCnBsb3QoZW5ldF9tb2QpCgoKYGBgCgpFTEFTVElDTkVUCgpgYGB7ciBlbGFzdGljbmV0fQoKbWlubG9zc3Bsb3QoZW5ldF9tb2QsIAogICAgICAgICAgICBjdi50eXBlID0gIm1pbiIpCgpnZXRfYWxwaGEgPC0gZnVuY3Rpb24oZml0KSB7CiAgYWxwaGEgPC0gZml0JGFscGhhCiAgZXJyb3IgPC0gc2FwcGx5KGZpdCRtb2RsaXN0LCAKICAgICAgICAgICAgICAgICAgZnVuY3Rpb24obW9kKSB7bWluKG1vZCRjdm0pfSkKICBhbHBoYVt3aGljaC5taW4oZXJyb3IpXQp9CgpnZXRfbW9kZWxfcGFyYW1zIDwtIGZ1bmN0aW9uKGZpdCkgewogIGFscGhhIDwtIGZpdCRhbHBoYQogIGxhbWJkYU1pbiA8LSBzYXBwbHkoZml0JG1vZGxpc3QsIGBbW2AsICJsYW1iZGEubWluIikKICBsYW1iZGFTRSA8LSBzYXBwbHkoZml0JG1vZGxpc3QsIGBbW2AsICJsYW1iZGEuMXNlIikKICBlcnJvciA8LSBzYXBwbHkoZml0JG1vZGxpc3QsIGZ1bmN0aW9uKG1vZCkge21pbihtb2QkY3ZtKX0pCiAgYmVzdCA8LSB3aGljaC5taW4oZXJyb3IpCiAgZGF0YS5mcmFtZShhbHBoYSA9IGFscGhhW2Jlc3RdLCBsYW1iZGFNaW4gPSBsYW1iZGFNaW5bYmVzdF0sCiAgICAgICAgICAgICBsYW1iZGFTRSA9IGxhbWJkYVNFW2Jlc3RdLCBlcm9yID0gZXJyb3JbYmVzdF0pCn0KCmJlc3RfYWxwaGEgPC0gZ2V0X2FscGhhKGVuZXRfbW9kKQpwcmludChiZXN0X2FscGhhKQpnZXRfbW9kZWxfcGFyYW1zKGVuZXRfbW9kKQoKYmVzdF9tb2QgPC0gZW5ldF9tb2QkbW9kbGlzdFtbd2hpY2goZW5ldF9tb2QkYWxwaGEgPT0gYmVzdF9hbHBoYSldXQoKcHJpbnQoYmVzdF9tb2QpCgptaW5sb3NzcGxvdChlbmV0X21vZCwgY3YudHlwZSA9ICJtaW4iKQoKYGBgCgpSaWRnZXMgcGxvdCAtIGNvdWxkIGJlIHVzZWZ1bCBmb3IgcGxvdHRpbmcgZG9uYXRpb25zIHZzIGRvbm9yIHNlZ21lbnQKCmBgYHtyfQoKbGlicmFyeSgnZ2dyaWRnZXMnKQoKc3VtbWFyeShkYXRhX3RyYWluJHZhcmlhYmxlKQoKZ2dwbG90KGRhdGFfdHJhaW4sIGFlcyh4ID0gSEguTGlmZXRpbWUuR2l2aW5nLCB5ID0gcmVnaW9uKSkgKyBnZW9tX2RlbnNpdHlfcmlkZ2VzKHJlbF9taW5faGVpZ2h0ID0gMC4wMDUpICsgeGxpbShjKDI1MDAwLCAxMDAwMDApKSArIAogICAgICBnZ3RpdGxlKCJISCBMaWZldGltZSBHaXZpbmcgYnkgUmVnaW9uIikKCmBgYAoKYGBge3J9CgpsaWJyYXJ5KCdjb3JycGxvdCcpCgojcmVtb3ZpbmcgSUQgemlwIGFuZCBub25udW1lcmljIApjb3JycGxvdF9kYXRhIDwtIGRhdGFjbGVhblstYygxOjQ4LDUyOjU2LDU4OjYwLDYzLDY2OjY3LDcwOjcyLDc0OjgxLDgzOjEzMildCgojQ29udmVydCBmcm9tIGNoYXJhY3RlciB0byBudW1lcmljIGRhdGEgdHlwZQpjb252ZXJ0X2ZhYzJudW0gPC0gZnVuY3Rpb24oeCl7CiAgYXMubnVtZXJpYyhhcy5mYWN0b3IoeCkpCn0KCmNvcnJwbG90X2RhdGEgPC0gbXV0YXRlX2F0KGNvcnJwbG90X2RhdGEsCiAgICAgICAgICAgICAgICAgICAgIC52YXJzID0gYygxOjEyKSwKICAgICAgICAgICAgICAgICAgICAgLmZ1bnMgPSBjb252ZXJ0X2ZhYzJudW0pCiNtYWtpbmcgYSBtYXRyaXgKY2RfY29yIDwtIGNvcihjb3JycGxvdF9kYXRhKQoKI2NyZWF0aW5nIGNvcnJlbGF0aW9uCmNvbCA8LSBjb2xvclJhbXBQYWxldHRlKGMoIiNCQjQ0MDAiLCAiI0VFOTk5MCIsIAogICIjRkZGRkZGIiwgIiM3N0FBRUUiLCAiIzQ0NzdCQiIpKQpjb3JycGxvdChjZF9jb3IsIG1ldGhvZD0iY29sb3IiLCBjb2w9Y29sKDEwMCksCiAgdHlwZT0ibG93ZXIiLCBhZGRDb2VmLmNvbCA9ICJibGFjayIsCiAgdGwucG9zPSJsdCIsIHRsLmNvbD0iYmxhY2siLCAKICB0bC5jZXg9MC43LCB0bC5zcnQ9NDUsIAogIG51bWJlci5jZXg9MC43LAogIGRpYWc9RkFMU0UpCgojY29ycmVsYXRpb24gbWF0cml4CiMgcGFpcnMofkFnZSArIE1vbnRocy5TaW5jZS5MYXN0LkdpZnQgKyBkb25vcnNlZ19mY3QgKyAKIyAgICAgbm1iX2RlZ3JlZSArIE5vX29mX0NoaWxkcmVuICsgSEguRmlyc3QuR2lmdC5BZ2UgKyBISC5GaXJzdC5HaWZ0LkFtb3VudCArIFRvdGFsLkdpdmluZy5ZZWFycywKIyAgICAgY29sID0gY29ycnBsb3RfZGF0YSRISC5MaWZldGltZS5HaXZpbmcsCiMgICAgIGRhdGEgPSBjb3JycGxvdF9kYXRhLCAKIyAgICAgbWFpbiA9ICJEb25vciBTY2F0dGVyIFBsb3QgTWF0cml4IikKCiN3b3J0aGxlc3MuLiAKCmdncGxvdChkYXRhID0gY29ycnBsb3RfZGF0YSwgYWVzKHggPSBubWJfZGVncmVlLCB5ID0gSEguTGlmZXRpbWUuR2l2aW5nKSkgKyAKICBnZW9tX3BvaW50KGFwbGhhID0gMS8xMCkrCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSJyZWQiKSAKCmBgYAoKClJhbmRvbSBGb3Jlc3QKCmBgYHtyfQoKbGlicmFyeSgncmFuZG9tRm9yZXN0JykKCnJmX2ZpdF9kb25vciA8LSByYW5kb21Gb3Jlc3QoTGlmZXRpbWUuR2l2aW5nIH4gLiwgCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFfdHJhaW4sCiAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9IGNsYXNzaWZpY2F0aW9uLAogICAgICAgICAgICAgICAgICAgICAgIG10cnkgPSA3LAogICAgICAgICAgICAgICAgICAgICAgIG5hLmFjdGlvbiA9IG5hLnJvdWdoZml4LAogICAgICAgICAgICAgICAgICAgICAgIG50cmVlID0gMjAwLAogICAgICAgICAgICAgICAgICAgICAgIGltcG9ydGFuY2U9VFJVRQogICAgICAgICAgICAgICAgICAgICAgICkKCnByaW50KHJmX2ZpdF9kb25vcikKCgpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNn0KCnZhckltcFBsb3QocmZfZml0X2Rvbm9yLCBzb3J0ID0gVFJVRSwgCiAgICAgICAgICAgbi52YXIgPSA1LAogICAgICAgICAgIHR5cGUgPSAyLCBjbGFzcyA9IE5VTEwsIHNjYWxlID0gVFJVRSwgCiAgICAgICAgICAgbWFpbiA9IGRlcGFyc2Uoc3Vic3RpdHV0ZShyZl9maXRfZG9ub3IpKSkKCgpgYGAKCmBgYHtyfQoKbGlicmFyeSgncmFuZG9tRm9yZXN0RXhwbGFpbmVyJykKCnBsb3RfbWluX2RlcHRoX2Rpc3RyaWJ1dGlvbigKICByZl9maXRfZG9ub3IsCiAgayA9IDEwLAogIG1pbl9ub19vZl90cmVlcyA9IDAsCiAgbWVhbl9zYW1wbGUgPSAidG9wX3RyZWVzIiwKICBtZWFuX3NjYWxlID0gRkFMU0UsCiAgbWVhbl9yb3VuZCA9IDIsCiAgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgbWluaW1hbCBkZXB0aCBhbmQgaXRzIG1lYW4iCikKCmBgYAoKCgoKYGBge3J9CiNTcGxpdHRpbmcgQ2F0ZWdvcnkgb3V0IHRvIGNoZWNrIGlmIHRoZSBjYXRlZ29yeSBpcyB1c2VmdWwgZm9yIGFuYWx5c2lzCmRhdGFfY2F0ZWdvcnlfc3BsaXRfb3V0IDwtIGRhdGFjbGVhbiAlPiUKICBtdXRhdGUoQ2F0ZWdvcnkuQ29kZXMgPSB0cmltKHN0cnNwbGl0KGFzLmNoYXJhY3RlcihDYXRlZ29yeS5Db2RlcyksICJ8IiwgZml4ZWQgPSBUUlVFKSkpICU+JQogIHVubmVzdChDYXRlZ29yeS5Db2RlcykgJT4lIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBDYXRlZ29yeS5Db2Rlcyx2YWx1ZXNfZnJvbSA9Q2F0ZWdvcnkuQ29kZXMsIHZhbHVlc19mbiA9IGxlbmd0aCkKCgpgYGAK